blueprint-tsa 1.0.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 +0 -0
- package/dist/cli.d.ts +13 -0
- package/dist/cli.js +54 -0
- package/dist/commands/audit.d.ts +19 -0
- package/dist/commands/audit.js +296 -0
- package/dist/commands/bounce-check.d.ts +15 -0
- package/dist/commands/bounce-check.js +152 -0
- package/dist/commands/clean.d.ts +1 -0
- package/dist/commands/clean.js +20 -0
- package/dist/commands/drain-check.d.ts +32 -0
- package/dist/commands/drain-check.js +218 -0
- package/dist/commands/opcode-info.d.ts +23 -0
- package/dist/commands/opcode-info.js +176 -0
- package/dist/commands/owner-hijack-check.d.ts +20 -0
- package/dist/commands/owner-hijack-check.js +290 -0
- package/dist/commands/replay-attack-check.d.ts +20 -0
- package/dist/commands/replay-attack-check.js +149 -0
- package/dist/commands/reproduce.d.ts +3 -0
- package/dist/commands/reproduce.js +102 -0
- package/dist/common/analyzer-wrapper.d.ts +69 -0
- package/dist/common/analyzer-wrapper.js +198 -0
- package/dist/common/analyzer.d.ts +10 -0
- package/dist/common/analyzer.js +49 -0
- package/dist/common/build-utils.d.ts +3 -0
- package/dist/common/build-utils.js +68 -0
- package/dist/common/constants.d.ts +41 -0
- package/dist/common/constants.js +45 -0
- package/dist/common/draw.d.ts +7 -0
- package/dist/common/draw.js +33 -0
- package/dist/common/file-utils.d.ts +7 -0
- package/dist/common/file-utils.js +20 -0
- package/dist/common/format-utils.d.ts +13 -0
- package/dist/common/format-utils.js +30 -0
- package/dist/common/opcode-extractor.d.ts +7 -0
- package/dist/common/opcode-extractor.js +60 -0
- package/dist/common/paths.d.ts +19 -0
- package/dist/common/paths.js +139 -0
- package/dist/common/result-parsing.d.ts +4 -0
- package/dist/common/result-parsing.js +40 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +16 -0
- package/dist/install/architecture.d.ts +5 -0
- package/dist/install/architecture.js +51 -0
- package/dist/install/downloading.d.ts +1 -0
- package/dist/install/downloading.js +44 -0
- package/dist/install/java.d.ts +1 -0
- package/dist/install/java.js +89 -0
- package/dist/install/postinstall.d.ts +1 -0
- package/dist/install/postinstall.js +12 -0
- package/dist/install/tsa-jar.d.ts +1 -0
- package/dist/install/tsa-jar.js +23 -0
- package/dist/install/unzip.d.ts +1 -0
- package/dist/install/unzip.js +14 -0
- package/dist/reproduce/build-config.d.ts +3 -0
- package/dist/reproduce/build-config.js +24 -0
- package/dist/reproduce/concrete-analysis.d.ts +15 -0
- package/dist/reproduce/concrete-analysis.js +21 -0
- package/dist/reproduce/network.d.ts +19 -0
- package/dist/reproduce/network.js +70 -0
- package/dist/reproduce/reproduce-config.d.ts +30 -0
- package/dist/reproduce/reproduce-config.js +59 -0
- package/dist/reproduce/utils.d.ts +4 -0
- package/dist/reproduce/utils.js +34 -0
- package/dist/tsa.d.ts +2 -0
- package/dist/tsa.js +22 -0
- package/package.json +45 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.drainCheckConcrete = exports.runDrainCheckAnalysis = exports.configureDrainCheckCommand = void 0;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const core_1 = require("@ton/core");
|
|
6
|
+
const analyzer_wrapper_js_1 = require("../common/analyzer-wrapper.js");
|
|
7
|
+
const build_config_js_1 = require("../reproduce/build-config.js");
|
|
8
|
+
const constants_js_1 = require("../common/constants.js");
|
|
9
|
+
const build_utils_js_1 = require("../common/build-utils.js");
|
|
10
|
+
const utils_js_1 = require("../reproduce/utils.js");
|
|
11
|
+
const paths_js_1 = require("../common/paths.js");
|
|
12
|
+
const opcode_extractor_js_1 = require("../common/opcode-extractor.js");
|
|
13
|
+
const ONE_MINUTE_SECONDS = 60;
|
|
14
|
+
const configureDrainCheckCommand = (context) => {
|
|
15
|
+
return {
|
|
16
|
+
command: constants_js_1.DRAIN_CHECK_ID,
|
|
17
|
+
description: "Analyze contract for drain vulnerabilities",
|
|
18
|
+
builder: (yargs) => yargs
|
|
19
|
+
.option("timeout", {
|
|
20
|
+
alias: "t",
|
|
21
|
+
type: "number",
|
|
22
|
+
description: "Analysis timeout in seconds",
|
|
23
|
+
})
|
|
24
|
+
.option("contract", {
|
|
25
|
+
alias: "c",
|
|
26
|
+
type: "string",
|
|
27
|
+
description: "Contract name",
|
|
28
|
+
demandOption: true,
|
|
29
|
+
})
|
|
30
|
+
.option("verbose", {
|
|
31
|
+
alias: "v",
|
|
32
|
+
type: "boolean",
|
|
33
|
+
description: "Use debug output in TSA log",
|
|
34
|
+
})
|
|
35
|
+
.option("disable-opcode-extraction", {
|
|
36
|
+
type: "boolean",
|
|
37
|
+
description: "Disable opcode extraction. This affects path selection strategy and default timeout.",
|
|
38
|
+
}),
|
|
39
|
+
handler: async (argv) => {
|
|
40
|
+
await drainCheckCommand(context, argv);
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
exports.configureDrainCheckCommand = configureDrainCheckCommand;
|
|
45
|
+
/**
|
|
46
|
+
* Runs drain check analysis and returns the analyzer wrapper
|
|
47
|
+
* @param contractName - Name of the contract
|
|
48
|
+
* @param contractPath - Path to the compiled contract
|
|
49
|
+
* @param ui - UI provider
|
|
50
|
+
* @param timeout - Analysis timeout in seconds
|
|
51
|
+
* @param opcodes - List of opcodes to analyze
|
|
52
|
+
* @param verbose - Enable verbose output
|
|
53
|
+
* @returns AnalyzerWrapper instance
|
|
54
|
+
*/
|
|
55
|
+
const runDrainCheckAnalysis = async (contractName, contractPath, ui, timeout, opcodes, verbose = false, completionMessage = "Analysis complete.") => {
|
|
56
|
+
const checkerPath = (0, paths_js_1.getCheckerPath)(constants_js_1.DRAIN_CHECK_SYMBOLIC_FILENAME);
|
|
57
|
+
const properties = [
|
|
58
|
+
{ key: "Contract", value: contractName },
|
|
59
|
+
{ key: "Mode", value: "TON drain" },
|
|
60
|
+
{
|
|
61
|
+
key: "Options",
|
|
62
|
+
separator: true,
|
|
63
|
+
children: [
|
|
64
|
+
{
|
|
65
|
+
key: "Timeout",
|
|
66
|
+
value: timeout !== null ? `${timeout} seconds` : "not set",
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
];
|
|
71
|
+
const checkerCell = (0, core_1.beginCell)().endCell();
|
|
72
|
+
const analyzer = new analyzer_wrapper_js_1.AnalyzerWrapper({
|
|
73
|
+
ui,
|
|
74
|
+
checkerPath,
|
|
75
|
+
checkerCell,
|
|
76
|
+
properties,
|
|
77
|
+
codePath: contractPath,
|
|
78
|
+
});
|
|
79
|
+
const reportDir = (0, paths_js_1.getReportDirectory)(analyzer.id);
|
|
80
|
+
const sarifPath = (0, paths_js_1.getSarifReportPath)(analyzer.id);
|
|
81
|
+
await analyzer.run(constants_js_1.DRAIN_CHECK_SYMBOLIC_FILENAME, (wrapper) => [
|
|
82
|
+
"custom-checker-compiled",
|
|
83
|
+
"--checker",
|
|
84
|
+
wrapper.getTempBocPath(),
|
|
85
|
+
"--contract",
|
|
86
|
+
contractPath,
|
|
87
|
+
"--stop-when-exit-codes-found",
|
|
88
|
+
constants_js_1.ERROR_EXIT_CODE.toString(),
|
|
89
|
+
"--checker-data",
|
|
90
|
+
wrapper.getTempCheckerCellPath(),
|
|
91
|
+
"--output",
|
|
92
|
+
sarifPath,
|
|
93
|
+
...(timeout != null ? ["--timeout", timeout.toString()] : []),
|
|
94
|
+
"--exported-inputs",
|
|
95
|
+
reportDir,
|
|
96
|
+
...(verbose ? ["-v"] : []),
|
|
97
|
+
...opcodes.flatMap((opcode) => ["--opcode", opcode.toString()]),
|
|
98
|
+
], completionMessage);
|
|
99
|
+
// Write reproduction config if vulnerability is found
|
|
100
|
+
const vulnerability = analyzer.getVulnerability();
|
|
101
|
+
if (vulnerability) {
|
|
102
|
+
(0, build_config_js_1.writeReproduceConfig)(vulnerability, constants_js_1.DRAIN_CHECK_ID, timeout, analyzer.id, {
|
|
103
|
+
kind: constants_js_1.DRAIN_CHECK_ID,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return analyzer;
|
|
107
|
+
};
|
|
108
|
+
exports.runDrainCheckAnalysis = runDrainCheckAnalysis;
|
|
109
|
+
const drainCheckCommand = async (context, parsedArgs) => {
|
|
110
|
+
const { ui } = context;
|
|
111
|
+
await (0, build_utils_js_1.buildContracts)(ui);
|
|
112
|
+
if (!parsedArgs.contract) {
|
|
113
|
+
throw new Error("Contract name or path is required");
|
|
114
|
+
}
|
|
115
|
+
const contract = parsedArgs.contract;
|
|
116
|
+
const contractPath = (0, paths_js_1.findCompiledContract)(contract);
|
|
117
|
+
if (!(0, fs_1.existsSync)(contractPath)) {
|
|
118
|
+
ui.write(`\n${constants_js_1.Sym.ERR} Contract ${contract} not found`);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
let opcodes = [];
|
|
122
|
+
if (!parsedArgs["disable-opcode-extraction"]) {
|
|
123
|
+
opcodes = await (0, opcode_extractor_js_1.extractOpcodes)({
|
|
124
|
+
ui,
|
|
125
|
+
codePath: contractPath,
|
|
126
|
+
contractName: contract,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
let timeout = parsedArgs.timeout ?? null;
|
|
130
|
+
// If timeout wasn't provided, set it to 1 minute * (number_of_opcodes + 1)
|
|
131
|
+
if (timeout === null && opcodes.length > 0) {
|
|
132
|
+
timeout = ONE_MINUTE_SECONDS * (opcodes.length + 1);
|
|
133
|
+
ui.write("");
|
|
134
|
+
ui.write("The timeout was calculated automatically based on the number of opcodes.");
|
|
135
|
+
}
|
|
136
|
+
const analyzer = await (0, exports.runDrainCheckAnalysis)(contract, contractPath, ui, timeout, opcodes, parsedArgs.verbose);
|
|
137
|
+
const vulnerability = analyzer.getVulnerability();
|
|
138
|
+
analyzer.reportVulnerability(vulnerability, constants_js_1.DRAIN_DESCRIPTION_URL);
|
|
139
|
+
(0, utils_js_1.printCleanupInstructions)(ui);
|
|
140
|
+
if (vulnerability != null) {
|
|
141
|
+
(0, utils_js_1.printReproductionInstructions)(ui, analyzer.id);
|
|
142
|
+
process.exit(2);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
const drainCheckConcrete = async (config, completionMessage = "Analysis complete.") => {
|
|
146
|
+
const { ui } = config;
|
|
147
|
+
if (!(0, fs_1.existsSync)(config.codePath)) {
|
|
148
|
+
ui.write(`\n${constants_js_1.Sym.ERR} Code at ${config.codePath} not found`);
|
|
149
|
+
process.exit(1);
|
|
150
|
+
}
|
|
151
|
+
const timeout = config.timeout;
|
|
152
|
+
const properties = [
|
|
153
|
+
{ key: "Contract", value: config.contractAddress.toRawString() },
|
|
154
|
+
{ key: "Mode", value: "TON drain reproduction" },
|
|
155
|
+
{
|
|
156
|
+
key: "Options",
|
|
157
|
+
separator: true,
|
|
158
|
+
children: [
|
|
159
|
+
{
|
|
160
|
+
key: "Timeout",
|
|
161
|
+
value: timeout !== null ? `${timeout} seconds` : "not set",
|
|
162
|
+
},
|
|
163
|
+
{ key: "Sender", value: config.senderAddress.toRawString() },
|
|
164
|
+
],
|
|
165
|
+
},
|
|
166
|
+
];
|
|
167
|
+
const maxTons = (0, core_1.toNano)(await ui.input("Enter maximum amount of TONs for reproduction message:"));
|
|
168
|
+
const checkerPath = (0, paths_js_1.getCheckerPath)(constants_js_1.DRAIN_CHECK_CONCRETE_FILENAME);
|
|
169
|
+
const checkerCell = (0, core_1.beginCell)()
|
|
170
|
+
.storeAddress(config.senderAddress)
|
|
171
|
+
.storeCoins(maxTons)
|
|
172
|
+
.endCell();
|
|
173
|
+
const analyzer = new analyzer_wrapper_js_1.AnalyzerWrapper({
|
|
174
|
+
ui,
|
|
175
|
+
checkerPath,
|
|
176
|
+
checkerCell,
|
|
177
|
+
properties,
|
|
178
|
+
codePath: config.codePath,
|
|
179
|
+
});
|
|
180
|
+
await analyzer.run(constants_js_1.DRAIN_CHECK_CONCRETE_FILENAME, (wrapper) => [
|
|
181
|
+
"custom-checker-compiled",
|
|
182
|
+
"--checker",
|
|
183
|
+
wrapper.getTempBocPath(),
|
|
184
|
+
"--contract",
|
|
185
|
+
config.codePath,
|
|
186
|
+
"--data",
|
|
187
|
+
config.dataPath,
|
|
188
|
+
"--balance",
|
|
189
|
+
config.balance.toString(),
|
|
190
|
+
"--address",
|
|
191
|
+
config.contractAddress.toRawString(),
|
|
192
|
+
"--stop-when-exit-codes-found",
|
|
193
|
+
constants_js_1.ERROR_EXIT_CODE.toString(),
|
|
194
|
+
"--checker-data",
|
|
195
|
+
wrapper.getTempCheckerCellPath(),
|
|
196
|
+
"--output",
|
|
197
|
+
(0, paths_js_1.getSarifReportPath)(wrapper.id),
|
|
198
|
+
"--exported-inputs",
|
|
199
|
+
(0, paths_js_1.getReportDirectory)(wrapper.id),
|
|
200
|
+
...(config.timeout != null
|
|
201
|
+
? ["--timeout", config.timeout.toString()]
|
|
202
|
+
: []),
|
|
203
|
+
], completionMessage);
|
|
204
|
+
const vulnerability = analyzer.getVulnerability();
|
|
205
|
+
if (vulnerability == null) {
|
|
206
|
+
ui.write(`${constants_js_1.Sym.WARN} Vulnerability couldn't be reproduced with concrete data.`);
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
if (vulnerability.value == null) {
|
|
210
|
+
throw new Error("Unexpected external message");
|
|
211
|
+
}
|
|
212
|
+
return {
|
|
213
|
+
address: config.contractAddress,
|
|
214
|
+
msgBody: vulnerability.msgBody,
|
|
215
|
+
suggestedValue: vulnerability.value,
|
|
216
|
+
};
|
|
217
|
+
};
|
|
218
|
+
exports.drainCheckConcrete = drainCheckConcrete;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Argv } from "yargs";
|
|
2
|
+
import yargs from "yargs";
|
|
3
|
+
import { CommandContext } from "../cli.js";
|
|
4
|
+
import { UIProvider } from "@ton/blueprint";
|
|
5
|
+
export interface OpcodeInfo {
|
|
6
|
+
opcode: number;
|
|
7
|
+
withAuthorization: boolean;
|
|
8
|
+
vulnerabilityPath?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function runOpcodeAuthorizationCheckAnalysis(opcode: number, contractName: string, contractPath: string, ui: UIProvider, timeout: number | null, completionMessage?: string, verbose?: boolean): Promise<OpcodeInfo | null>;
|
|
11
|
+
export declare function formatOpcodeInfo(infos: OpcodeInfo[]): string;
|
|
12
|
+
export declare const configureOpcodeInfoCommand: (context: CommandContext) => {
|
|
13
|
+
command: string;
|
|
14
|
+
description: string;
|
|
15
|
+
builder: (yargs: Argv) => Argv<{
|
|
16
|
+
contract: string;
|
|
17
|
+
} & {
|
|
18
|
+
timeout: number;
|
|
19
|
+
} & {
|
|
20
|
+
verbose: boolean | undefined;
|
|
21
|
+
}>;
|
|
22
|
+
handler: (argv: yargs.ArgumentsCamelCase) => Promise<void>;
|
|
23
|
+
};
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.configureOpcodeInfoCommand = void 0;
|
|
7
|
+
exports.runOpcodeAuthorizationCheckAnalysis = runOpcodeAuthorizationCheckAnalysis;
|
|
8
|
+
exports.formatOpcodeInfo = formatOpcodeInfo;
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const constants_js_1 = require("../common/constants.js");
|
|
11
|
+
const opcode_extractor_js_1 = require("../common/opcode-extractor.js");
|
|
12
|
+
const build_utils_js_1 = require("../common/build-utils.js");
|
|
13
|
+
const paths_js_1 = require("../common/paths.js");
|
|
14
|
+
const fs_1 = require("fs");
|
|
15
|
+
const analyzer_wrapper_js_1 = require("../common/analyzer-wrapper.js");
|
|
16
|
+
const core_1 = require("@ton/core");
|
|
17
|
+
const format_utils_js_1 = require("../common/format-utils.js");
|
|
18
|
+
const result_parsing_js_1 = require("../common/result-parsing.js");
|
|
19
|
+
async function runOpcodeAuthorizationCheckAnalysis(opcode, contractName, contractPath, ui, timeout, completionMessage = "Analysis complete.", verbose = false) {
|
|
20
|
+
const properties = [
|
|
21
|
+
{ key: "Contract", value: contractName },
|
|
22
|
+
{ key: "Mode", value: "Opcode Authorization Check" },
|
|
23
|
+
{
|
|
24
|
+
key: "Options",
|
|
25
|
+
separator: true,
|
|
26
|
+
children: [
|
|
27
|
+
{
|
|
28
|
+
key: "Opcode",
|
|
29
|
+
value: (0, format_utils_js_1.formatOpcodeHex)(opcode),
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
key: "Timeout",
|
|
33
|
+
value: timeout !== null ? `${timeout} seconds` : "not set",
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
const checkerPath = (0, paths_js_1.getCheckerPath)(constants_js_1.OPCODE_AUTHORIZATION_CHECK_FILENAME);
|
|
39
|
+
const checkerCell = (0, core_1.beginCell)().storeUint(opcode, 32).endCell();
|
|
40
|
+
const analyzer = new analyzer_wrapper_js_1.AnalyzerWrapper({
|
|
41
|
+
ui,
|
|
42
|
+
checkerPath,
|
|
43
|
+
checkerCell,
|
|
44
|
+
properties,
|
|
45
|
+
codePath: contractPath,
|
|
46
|
+
});
|
|
47
|
+
const sarifPath = (0, paths_js_1.getSarifReportPath)(analyzer.id);
|
|
48
|
+
await analyzer.run(constants_js_1.OPCODE_AUTHORIZATION_CHECK_FILENAME, (wrapper) => [
|
|
49
|
+
"custom-checker-compiled",
|
|
50
|
+
"--checker",
|
|
51
|
+
wrapper.getTempBocPath(),
|
|
52
|
+
"--contract",
|
|
53
|
+
contractPath,
|
|
54
|
+
"--stop-when-exit-codes-found",
|
|
55
|
+
constants_js_1.ERROR_EXIT_CODE.toString(),
|
|
56
|
+
"--checker-data",
|
|
57
|
+
wrapper.getTempCheckerCellPath(),
|
|
58
|
+
"--output",
|
|
59
|
+
sarifPath,
|
|
60
|
+
...(timeout != null ? ["--timeout", timeout.toString()] : []),
|
|
61
|
+
"--disable-out-message-analysis",
|
|
62
|
+
"--exported-inputs",
|
|
63
|
+
(0, paths_js_1.getReportDirectory)(wrapper.id),
|
|
64
|
+
...(verbose ? ["-v"] : []),
|
|
65
|
+
], completionMessage);
|
|
66
|
+
const vulnerability = analyzer.vulnerabilityIsPresent();
|
|
67
|
+
const nonFailingExecutionIndex = (0, result_parsing_js_1.findNonFailingExecution)(sarifPath);
|
|
68
|
+
if (nonFailingExecutionIndex === undefined && !vulnerability) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
const withAuthorization = !vulnerability;
|
|
72
|
+
let vulnerabilityPath;
|
|
73
|
+
if (vulnerability) {
|
|
74
|
+
const vulnDesc = analyzer.getVulnerability();
|
|
75
|
+
if (vulnDesc) {
|
|
76
|
+
vulnerabilityPath = (0, paths_js_1.getInputsPath)(analyzer.id, vulnDesc.executionIndex);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
opcode,
|
|
81
|
+
withAuthorization,
|
|
82
|
+
vulnerabilityPath,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
async function extractOpcodeInfo(opcode, contractName, contractPath, ui, timeout, verbose) {
|
|
86
|
+
return runOpcodeAuthorizationCheckAnalysis(opcode, contractName, contractPath, ui, timeout, "Analysis complete.", verbose);
|
|
87
|
+
}
|
|
88
|
+
async function getAllOpcodeInfo(opcodes, contractName, contractPath, ui, timeout, verbose) {
|
|
89
|
+
const results = [];
|
|
90
|
+
for (const opcode of opcodes) {
|
|
91
|
+
const info = await extractOpcodeInfo(opcode, contractName, contractPath, ui, timeout, verbose);
|
|
92
|
+
if (info !== null) {
|
|
93
|
+
results.push(info);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return results;
|
|
97
|
+
}
|
|
98
|
+
function formatOpcodeInfo(infos) {
|
|
99
|
+
if (infos.length === 0) {
|
|
100
|
+
return "No opcodes to analyze.";
|
|
101
|
+
}
|
|
102
|
+
const lines = ["Opcode Authorization Analysis:", ""];
|
|
103
|
+
let hasUnauthorizedOpcodes = false;
|
|
104
|
+
for (const info of infos) {
|
|
105
|
+
const opcodeHex = (0, format_utils_js_1.formatOpcodeHex)(info.opcode);
|
|
106
|
+
const authStatus = info.withAuthorization
|
|
107
|
+
? `${constants_js_1.Sym.OK} Has authorization checks`
|
|
108
|
+
: `${constants_js_1.Sym.WARN} No authorization checks`;
|
|
109
|
+
lines.push(`${opcodeHex}: ${authStatus}`);
|
|
110
|
+
// If authorization is missing and vulnerability path is available, show it
|
|
111
|
+
if (!info.withAuthorization && info.vulnerabilityPath) {
|
|
112
|
+
const relativePath = path_1.default.relative(process.cwd(), info.vulnerabilityPath);
|
|
113
|
+
lines.push(` Path to reproducing input: ${relativePath}`);
|
|
114
|
+
hasUnauthorizedOpcodes = true;
|
|
115
|
+
}
|
|
116
|
+
lines.push("");
|
|
117
|
+
}
|
|
118
|
+
// Add description URL if any opcodes lack authorization
|
|
119
|
+
if (hasUnauthorizedOpcodes) {
|
|
120
|
+
lines.push(`Description: ${constants_js_1.OPCODE_INFO_DESCRIPTION_URL}`);
|
|
121
|
+
}
|
|
122
|
+
lines.push("");
|
|
123
|
+
return lines.join("\n");
|
|
124
|
+
}
|
|
125
|
+
const opcodeInfoHandler = async (context, args) => {
|
|
126
|
+
const { ui } = context;
|
|
127
|
+
const { timeout, contract, verbose } = args;
|
|
128
|
+
await (0, build_utils_js_1.buildContracts)(ui);
|
|
129
|
+
const codePath = (0, paths_js_1.findCompiledContract)(contract);
|
|
130
|
+
if (!(0, fs_1.existsSync)(codePath)) {
|
|
131
|
+
ui.write(`\n${constants_js_1.Sym.ERR} Contract ${contract} not found`);
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
const opcodes = await (0, opcode_extractor_js_1.extractOpcodes)({
|
|
135
|
+
ui,
|
|
136
|
+
codePath,
|
|
137
|
+
contractName: contract,
|
|
138
|
+
});
|
|
139
|
+
if (opcodes.length === 0) {
|
|
140
|
+
ui.write("");
|
|
141
|
+
ui.write(`${constants_js_1.Sym.WARN} No opcodes found in contract`);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const infos = await getAllOpcodeInfo(opcodes, contract, codePath, ui, timeout ?? null, verbose);
|
|
145
|
+
ui.write("");
|
|
146
|
+
const output = formatOpcodeInfo(infos);
|
|
147
|
+
ui.write(output);
|
|
148
|
+
};
|
|
149
|
+
const configureOpcodeInfoCommand = (context) => {
|
|
150
|
+
return {
|
|
151
|
+
command: constants_js_1.OPCODE_INFO,
|
|
152
|
+
description: "Display information about contract opcodes",
|
|
153
|
+
builder: (yargs) => yargs
|
|
154
|
+
.option("contract", {
|
|
155
|
+
alias: "c",
|
|
156
|
+
type: "string",
|
|
157
|
+
description: "Contract name",
|
|
158
|
+
demandOption: true,
|
|
159
|
+
})
|
|
160
|
+
.option("timeout", {
|
|
161
|
+
alias: "t",
|
|
162
|
+
type: "number",
|
|
163
|
+
description: "Timeout in seconds for analyzing one opcode",
|
|
164
|
+
default: 60,
|
|
165
|
+
})
|
|
166
|
+
.option("verbose", {
|
|
167
|
+
alias: "v",
|
|
168
|
+
type: "boolean",
|
|
169
|
+
description: "Use debug output in TSA log",
|
|
170
|
+
}),
|
|
171
|
+
handler: async (argv) => {
|
|
172
|
+
await opcodeInfoHandler(context, argv);
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
};
|
|
176
|
+
exports.configureOpcodeInfoCommand = configureOpcodeInfoCommand;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { CommandContext } from "../cli.js";
|
|
2
|
+
import { AnalyzerWrapper } from "../common/analyzer-wrapper.js";
|
|
3
|
+
import { UIProvider } from "@ton/blueprint";
|
|
4
|
+
import { ConcreteAnalysisConfig } from "../reproduce/concrete-analysis.js";
|
|
5
|
+
import { ReproduceParameters } from "../reproduce/network.js";
|
|
6
|
+
import { OwnerHijackOptions } from "../reproduce/reproduce-config.js";
|
|
7
|
+
export declare const configureOwnerHijackCommand: (context: CommandContext) => any;
|
|
8
|
+
/**
|
|
9
|
+
* Runs owner hijack check analysis and returns the analyzer wrapper
|
|
10
|
+
* @param contractName - Name of the contract
|
|
11
|
+
* @param contractPath - Path to the compiled contract
|
|
12
|
+
* @param ui - UI provider
|
|
13
|
+
* @param timeout - Analysis timeout in seconds
|
|
14
|
+
* @param methodId - Method ID of the owner getter
|
|
15
|
+
* @param opcodes - List of opcodes to analyze
|
|
16
|
+
* @param verbose - Enable verbose output
|
|
17
|
+
* @returns AnalyzerWrapper instance
|
|
18
|
+
*/
|
|
19
|
+
export declare const runOwnerHijackCheckAnalysis: (contractName: string, contractPath: string, ui: UIProvider, timeout: number | null, methodId: bigint, opcodes: number[], verbose?: boolean, completionMessage?: string) => Promise<AnalyzerWrapper>;
|
|
20
|
+
export declare const ownerHijackCheckConcrete: (config: ConcreteAnalysisConfig, concreteCheckerOptions: OwnerHijackOptions, completionMessage?: string) => Promise<ReproduceParameters | null>;
|