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.
- package/README.md +31 -0
- package/dist/cli.js +8 -16
- package/dist/commands/audit.d.ts +50 -17
- package/dist/commands/audit.js +113 -67
- package/dist/commands/bounce-check.d.ts +51 -7
- package/dist/commands/bounce-check.js +49 -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 +165 -0
- package/dist/commands/common-analyzer-args.d.ts +100 -0
- package/dist/commands/common-analyzer-args.js +89 -0
- package/dist/commands/create-checker.d.ts +11 -0
- package/dist/commands/create-checker.js +70 -0
- package/dist/commands/drain-check.d.ts +51 -21
- package/dist/commands/drain-check.js +63 -86
- package/dist/commands/opcode-info.d.ts +45 -15
- package/dist/commands/opcode-info.js +71 -46
- package/dist/commands/owner-hijack-check.d.ts +61 -11
- package/dist/commands/owner-hijack-check.js +76 -149
- package/dist/commands/replay-attack-check.d.ts +58 -6
- package/dist/commands/replay-attack-check.js +61 -74
- package/dist/commands/reproduce.d.ts +12 -2
- package/dist/commands/reproduce.js +20 -13
- package/dist/common/analyzer-wrapper.d.ts +20 -5
- package/dist/common/analyzer-wrapper.js +235 -22
- package/dist/common/analyzer.js +1 -1
- package/dist/common/build-utils.d.ts +1 -1
- package/dist/common/build-utils.js +10 -5
- package/dist/common/constants.d.ts +5 -3
- package/dist/common/constants.js +4 -2
- package/dist/common/opcode-extractor.d.ts +1 -0
- package/dist/common/opcode-extractor.js +2 -0
- package/dist/common/paths.d.ts +5 -0
- package/dist/common/paths.js +31 -8
- package/dist/common/result-parsing.d.ts +2 -0
- package/dist/common/result-parsing.js +22 -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, 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,
|
|
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,
|
|
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);
|
|
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)(
|
|
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,
|
|
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)(
|
|
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,
|
|
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);
|
|
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
|
|
195
|
-
const
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
const
|
|
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 ${
|
|
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
|
|
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:
|
|
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}
|
|
228
|
-
summary.opcodeInfo = await runOpcodeInfoCheck(
|
|
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}
|
|
232
|
-
const
|
|
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}
|
|
237
|
-
const replayResult = await runReplayAttackCheck(
|
|
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}
|
|
242
|
-
const bounceResult = await runBounceCheck(
|
|
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}
|
|
248
|
-
const ownerResult = await runOwnerHijackCheck(
|
|
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
|
|
332
|
+
const createAuditCommand = (context) => {
|
|
262
333
|
return {
|
|
263
334
|
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
|
-
}),
|
|
335
|
+
describe: "Run all available security checks and print a summary",
|
|
336
|
+
builder: auditOptions,
|
|
291
337
|
handler: async (argv) => {
|
|
292
|
-
await
|
|
338
|
+
await auditCommand(context.ui, argv);
|
|
293
339
|
},
|
|
294
340
|
};
|
|
295
341
|
};
|
|
296
|
-
exports.
|
|
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
|
-
|
|
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 {};
|