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.
- package/README.md +31 -0
- package/dist/cli.js +8 -16
- package/dist/commands/audit.d.ts +50 -17
- package/dist/commands/audit.js +110 -67
- package/dist/commands/bounce-check.d.ts +51 -7
- package/dist/commands/bounce-check.js +48 -74
- package/dist/commands/clean.d.ts +2 -1
- package/dist/commands/clean.js +7 -7
- package/dist/commands/command-utils.d.ts +34 -0
- package/dist/commands/command-utils.js +162 -0
- package/dist/commands/common-analyzer-args.d.ts +100 -0
- package/dist/commands/common-analyzer-args.js +89 -0
- package/dist/commands/drain-check.d.ts +51 -21
- package/dist/commands/drain-check.js +61 -86
- package/dist/commands/opcode-info.d.ts +45 -15
- package/dist/commands/opcode-info.js +69 -46
- package/dist/commands/owner-hijack-check.d.ts +61 -11
- package/dist/commands/owner-hijack-check.js +74 -149
- package/dist/commands/replay-attack-check.d.ts +58 -6
- package/dist/commands/replay-attack-check.js +60 -74
- package/dist/commands/reproduce.d.ts +12 -2
- package/dist/commands/reproduce.js +20 -13
- package/dist/common/analyzer-wrapper.d.ts +18 -5
- package/dist/common/analyzer-wrapper.js +219 -14
- package/dist/common/analyzer.js +1 -1
- package/dist/common/build-utils.d.ts +1 -1
- package/dist/common/build-utils.js +4 -4
- package/dist/common/constants.d.ts +5 -3
- package/dist/common/constants.js +4 -2
- package/dist/common/opcode-extractor.js +1 -0
- package/dist/common/paths.d.ts +5 -0
- package/dist/common/paths.js +31 -8
- package/dist/common/result-parsing.d.ts +1 -0
- package/dist/common/result-parsing.js +16 -10
- package/dist/reproduce/build-config.d.ts +1 -1
- package/dist/reproduce/build-config.js +3 -1
- package/dist/reproduce/concrete-analysis.d.ts +3 -2
- package/dist/reproduce/concrete-analysis.js +3 -4
- package/dist/reproduce/reproduce-config.d.ts +2 -0
- package/dist/reproduce/reproduce-config.js +2 -0
- 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(
|
|
33
|
-
.command(
|
|
34
|
-
.command(
|
|
35
|
-
.command(
|
|
36
|
-
.command(
|
|
37
|
-
.command(
|
|
38
|
-
.command(
|
|
39
|
-
.command(
|
|
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")
|
package/dist/commands/audit.d.ts
CHANGED
|
@@ -1,19 +1,52 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import yargs from "yargs";
|
|
1
|
+
import { CommandModule, InferredOptionTypes } from "yargs";
|
|
3
2
|
import { CommandContext } from "../cli.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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 {};
|
package/dist/commands/audit.js
CHANGED
|
@@ -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.
|
|
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
|
|
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.
|
|
81
|
+
const vulnDesc = analyzer.getVulnerabilityFromReport();
|
|
73
82
|
if (vulnDesc) {
|
|
74
|
-
result.vulnerabilityPath =
|
|
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,
|
|
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,
|
|
99
|
-
const analyzer = await (0, drain_check_js_1.runDrainCheckAnalysis)(
|
|
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)(
|
|
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,
|
|
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)(
|
|
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,
|
|
112
|
-
const analyzer = await (0, bounce_check_js_1.runBounceCheckAnalysis)(
|
|
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
|
|
195
|
-
const
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
const
|
|
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 ${
|
|
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
|
|
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:
|
|
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}
|
|
228
|
-
summary.opcodeInfo = await runOpcodeInfoCheck(
|
|
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}
|
|
232
|
-
const
|
|
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}
|
|
237
|
-
const replayResult = await runReplayAttackCheck(
|
|
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}
|
|
242
|
-
const bounceResult = await runBounceCheck(
|
|
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}
|
|
248
|
-
const ownerResult = await runOwnerHijackCheck(
|
|
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
|
|
329
|
+
const createAuditCommand = (context) => {
|
|
262
330
|
return {
|
|
263
331
|
command: constants_js_1.AUDIT_ID,
|
|
264
|
-
|
|
265
|
-
builder:
|
|
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
|
|
335
|
+
await auditCommand(context.ui, argv);
|
|
293
336
|
},
|
|
294
337
|
};
|
|
295
338
|
};
|
|
296
|
-
exports.
|
|
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
|
-
|
|
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
|
|
11
|
-
* @param
|
|
12
|
-
* @param
|
|
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: (
|
|
58
|
+
export declare const runBounceCheckAnalysis: (ui: UIProvider, contractPath: string, commonArgs: CommonAnalyzerRecvInternalArgs, completionMessage?: string) => Promise<AnalyzerWrapper>;
|
|
59
|
+
export {};
|