blueprint-tsa 1.0.4 → 1.1.0

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 (41) 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 +110 -67
  5. package/dist/commands/bounce-check.d.ts +51 -7
  6. package/dist/commands/bounce-check.js +48 -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 +162 -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/drain-check.d.ts +51 -21
  14. package/dist/commands/drain-check.js +61 -86
  15. package/dist/commands/opcode-info.d.ts +45 -15
  16. package/dist/commands/opcode-info.js +69 -46
  17. package/dist/commands/owner-hijack-check.d.ts +61 -11
  18. package/dist/commands/owner-hijack-check.js +74 -149
  19. package/dist/commands/replay-attack-check.d.ts +58 -6
  20. package/dist/commands/replay-attack-check.js +60 -74
  21. package/dist/commands/reproduce.d.ts +12 -2
  22. package/dist/commands/reproduce.js +20 -13
  23. package/dist/common/analyzer-wrapper.d.ts +18 -5
  24. package/dist/common/analyzer-wrapper.js +219 -14
  25. package/dist/common/analyzer.js +1 -1
  26. package/dist/common/build-utils.d.ts +1 -1
  27. package/dist/common/build-utils.js +4 -4
  28. package/dist/common/constants.d.ts +5 -3
  29. package/dist/common/constants.js +4 -2
  30. package/dist/common/opcode-extractor.js +1 -0
  31. package/dist/common/paths.d.ts +5 -0
  32. package/dist/common/paths.js +31 -8
  33. package/dist/common/result-parsing.d.ts +1 -0
  34. package/dist/common/result-parsing.js +16 -10
  35. package/dist/reproduce/build-config.d.ts +1 -1
  36. package/dist/reproduce/build-config.js +3 -1
  37. package/dist/reproduce/concrete-analysis.d.ts +3 -2
  38. package/dist/reproduce/concrete-analysis.js +3 -4
  39. package/dist/reproduce/reproduce-config.d.ts +2 -0
  40. package/dist/reproduce/reproduce-config.js +2 -0
  41. 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) {
82
93
  // Calculate timeout per opcode
83
94
  let opcodeTimeout = null;
84
95
  if (timeout !== null && opcodes.length > 0) {
@@ -86,31 +97,46 @@ 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
+ legacyAnalysisArtifacts,
107
+ };
89
108
  const results = [];
90
109
  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);
110
+ 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
111
  if (info !== null) {
93
112
  results.push(info);
94
113
  }
95
114
  }
96
115
  return results;
97
116
  }
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);
117
+ async function runDrainCheck(contractName, contractPath, ui, commonArgs) {
118
+ const analyzer = await (0, drain_check_js_1.runDrainCheckAnalysis)(ui, contractPath, commonArgs, `${constants_js_1.DRAIN_CHECK_NAME} completed.`);
119
+ 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
120
  }
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.`);
121
+ async function runReplayAttackCheck(contractName, contractPath, ui, timeout, verbose, iterationLimit, recursionLimit, legacyAnalysisArtifacts) {
122
+ const analyzer = await (0, replay_attack_check_js_1.runReplayAttackCheckAnalysis)(ui, contractPath, {
123
+ timeout,
124
+ verbose,
125
+ contract: contractName,
126
+ iterationLimit,
127
+ recursionLimit,
128
+ legacyAnalysisArtifacts,
129
+ }, null, `${constants_js_1.REPLAY_ATTACK_CHECK_NAME} completed.`);
104
130
  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
131
  }
106
- async function runOwnerHijackCheck(contractName, contractPath, ui, timeout, methodName, opcodes, verbose) {
132
+ async function runOwnerHijackCheck(contractName, contractPath, ui, methodName, commonArgs) {
107
133
  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);
134
+ const analyzer = await (0, owner_hijack_check_js_1.runOwnerHijackCheckAnalysis)(ui, contractPath, methodId, commonArgs, `${constants_js_1.OWNER_HIJACK_CHECK_NAME} completed.`);
135
+ 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
136
  }
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);
137
+ async function runBounceCheck(contractName, contractPath, ui, commonArgs) {
138
+ const analyzer = await (0, bounce_check_js_1.runBounceCheckAnalysis)(ui, contractPath, commonArgs, `${constants_js_1.BOUNCE_CHECK_NAME} completed.`);
139
+ 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
140
  }
115
141
  function buildAuditReport(summary) {
116
142
  const lines = [];
@@ -191,61 +217,103 @@ function saveAuditReport(summary, ui) {
191
217
  ui.write(`${constants_js_1.Sym.OK} Report saved to: ${filePath}`);
192
218
  return filePath;
193
219
  }
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);
220
+ const auditCommand = async (ui, parsedArgs) => {
221
+ const contractName = parsedArgs.contract;
222
+ const ownerMethod = parsedArgs["owner-method"];
223
+ const disableOpcodeExtraction = parsedArgs["disable-opcode-extraction"];
224
+ const verbose = parsedArgs.verbose;
225
+ await (0, build_utils_js_1.buildAllContracts)(ui);
226
+ const contractPath = (0, paths_js_1.findCompiledContract)(contractName);
199
227
  if (!(0, fs_1.existsSync)(contractPath)) {
200
- ui.write(`\n${constants_js_1.Sym.ERR} Contract ${contract} not found`);
228
+ ui.write(`\n${constants_js_1.Sym.ERR} Contract ${contractName} not found`);
201
229
  process.exit(1);
202
230
  }
231
+ const checkCount = ownerMethod ? 5 : 4;
232
+ const explicitTimeout = parsedArgs.timeout ?? null;
233
+ const hasUserProvidedTimeout = (0, command_utils_js_1.hasExplicitTimeout)(explicitTimeout);
234
+ if (hasUserProvidedTimeout && explicitTimeout !== null) {
235
+ await (0, command_utils_js_1.confirmLongRunningAnalysis)(ui, {
236
+ commandLabel: constants_js_1.AUDIT_ID,
237
+ contractName,
238
+ timeoutSeconds: explicitTimeout * checkCount,
239
+ checkCount,
240
+ interactive: parsedArgs.interactive,
241
+ });
242
+ }
203
243
  // Extract opcodes if not disabled
204
244
  let opcodes = [];
205
245
  if (!disableOpcodeExtraction) {
246
+ if (!hasUserProvidedTimeout) {
247
+ await (0, command_utils_js_1.confirmOpcodeExtractionWait)(ui, {
248
+ commandLabel: constants_js_1.AUDIT_ID,
249
+ contractName,
250
+ interactive: parsedArgs.interactive,
251
+ });
252
+ }
206
253
  opcodes = await (0, opcode_extractor_js_1.extractOpcodes)({
207
254
  ui,
208
255
  codePath: contractPath,
209
- contractName: contract,
256
+ contractName,
210
257
  });
211
258
  }
212
259
  // Calculate timeout if not provided
213
260
  // Timeout is per analyzer run (except for opcode-info where it's divided by number of opcodes)
214
- let effectiveTimeout = timeout ?? null;
261
+ let effectiveTimeout = parsedArgs.timeout ?? null;
215
262
  if (effectiveTimeout === null && opcodes.length > 0) {
216
- effectiveTimeout = ONE_MINUTE_SECONDS * (opcodes.length + 1);
263
+ effectiveTimeout = command_utils_js_1.ONE_MINUTE_SECONDS * (opcodes.length + 1);
217
264
  ui.write("");
218
265
  ui.write(`Timeout was calculated automatically: ${effectiveTimeout} seconds per analyzer run (based on ${opcodes.length} opcodes)`);
219
266
  }
267
+ const totalEstimatedTimeout = effectiveTimeout === null ? null : effectiveTimeout * checkCount;
268
+ if (!hasUserProvidedTimeout) {
269
+ await (0, command_utils_js_1.confirmLongRunningAnalysis)(ui, {
270
+ commandLabel: constants_js_1.AUDIT_ID,
271
+ contractName,
272
+ timeoutSeconds: totalEstimatedTimeout,
273
+ opcodeCount: opcodes.length,
274
+ checkCount,
275
+ interactive: parsedArgs.interactive,
276
+ });
277
+ }
220
278
  const summary = {
221
- contract: contract,
279
+ contract: contractName,
222
280
  checks: [],
223
281
  opcodeInfo: [],
224
282
  };
225
283
  // Run opcode-info check
226
284
  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);
285
+ ui.write(`${constants_js_1.Sym.WAIT} Step 1/${checkCount}: running opcode authorization analysis...`);
286
+ 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]);
229
287
  // Run drain-check
230
288
  ui.write("");
231
- ui.write(`${constants_js_1.Sym.WAIT} Running drain check...`);
232
- const drainResult = await runDrainCheck(contract, contractPath, ui, effectiveTimeout, opcodes, verbose);
289
+ ui.write(`${constants_js_1.Sym.WAIT} Step 2/${checkCount}: running drain check...`);
290
+ const commonArgs = {
291
+ timeout: effectiveTimeout,
292
+ opcodes,
293
+ verbose,
294
+ contract: contractName,
295
+ iterationLimit: parsedArgs[common_analyzer_args_js_1.ITERATION_LIMIT_OPTION],
296
+ recursionLimit: parsedArgs[common_analyzer_args_js_1.RECURSION_LIMIT_OPTION],
297
+ interactive: parsedArgs.interactive,
298
+ legacyAnalysisArtifacts: parsedArgs[common_analyzer_args_js_1.VERBOSE_ANALYSIS_ARTIFACTS_OPTION],
299
+ };
300
+ const drainResult = await runDrainCheck(contractName, contractPath, ui, commonArgs);
233
301
  summary.checks.push(drainResult);
234
302
  // Run replay-attack-check
235
303
  ui.write("");
236
- ui.write(`${constants_js_1.Sym.WAIT} Running replay attack check...`);
237
- const replayResult = await runReplayAttackCheck(contract, contractPath, ui, effectiveTimeout, verbose);
304
+ ui.write(`${constants_js_1.Sym.WAIT} Step 3/${checkCount}: running replay attack check...`);
305
+ const replayResult = await runReplayAttackCheck(contractName, contractPath, ui, effectiveTimeout, verbose ?? false, commonArgs.iterationLimit, commonArgs.recursionLimit, commonArgs.legacyAnalysisArtifacts ?? false);
238
306
  summary.checks.push(replayResult);
239
307
  // Run bounce-check
240
308
  ui.write("");
241
- ui.write(`${constants_js_1.Sym.WAIT} Running bounce check...`);
242
- const bounceResult = await runBounceCheck(contract, contractPath, ui, effectiveTimeout, opcodes, verbose);
309
+ ui.write(`${constants_js_1.Sym.WAIT} Step 4/${checkCount}: running bounce check...`);
310
+ const bounceResult = await runBounceCheck(contractName, contractPath, ui, commonArgs);
243
311
  summary.checks.push(bounceResult);
244
312
  // Run owner-hijack-check if owner method is provided
245
313
  if (ownerMethod) {
246
314
  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);
315
+ ui.write(`${constants_js_1.Sym.WAIT} Step 5/${checkCount}: running owner hijack check...`);
316
+ const ownerResult = await runOwnerHijackCheck(contractName, contractPath, ui, ownerMethod, commonArgs);
249
317
  summary.checks.push(ownerResult);
250
318
  }
251
319
  else {
@@ -258,39 +326,14 @@ const auditHandler = async (context, args) => {
258
326
  saveAuditReport(summary, ui);
259
327
  (0, utils_js_1.printCleanupInstructions)(ui);
260
328
  };
261
- const configureAuditCommand = (context) => {
329
+ const createAuditCommand = (context) => {
262
330
  return {
263
331
  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
- }),
332
+ describe: "Run all available security checks and print a summary",
333
+ builder: auditOptions,
291
334
  handler: async (argv) => {
292
- await auditHandler(context, argv);
335
+ await auditCommand(context.ui, argv);
293
336
  },
294
337
  };
295
338
  };
296
- exports.configureAuditCommand = configureAuditCommand;
339
+ 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 {};