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,198 @@
|
|
|
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.AnalyzerWrapper = void 0;
|
|
7
|
+
const fs_1 = require("fs");
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const os_1 = require("os");
|
|
10
|
+
const core_1 = require("@ton/core");
|
|
11
|
+
const constants_js_1 = require("./constants.js");
|
|
12
|
+
const build_utils_js_1 = require("./build-utils.js");
|
|
13
|
+
const analyzer_js_1 = require("./analyzer.js");
|
|
14
|
+
const draw_js_1 = require("./draw.js");
|
|
15
|
+
const format_utils_js_1 = require("./format-utils.js");
|
|
16
|
+
const result_parsing_js_1 = require("./result-parsing.js");
|
|
17
|
+
const paths_js_1 = require("./paths.js");
|
|
18
|
+
/**
|
|
19
|
+
* Generic wrapper for checker-based vulnerability analysis
|
|
20
|
+
* Handles common logic for compiling, running, and cleaning up checker analysis
|
|
21
|
+
*/
|
|
22
|
+
class AnalyzerWrapper {
|
|
23
|
+
constructor(config) {
|
|
24
|
+
this.tempBocPath = null;
|
|
25
|
+
this.tempCheckerCellPath = null;
|
|
26
|
+
this.config = config;
|
|
27
|
+
this.id = (0, format_utils_js_1.generateReportId)();
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Prints analysis information to UI
|
|
31
|
+
*/
|
|
32
|
+
printAnalysisInfo() {
|
|
33
|
+
if (!this.config.properties) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const output = (0, draw_js_1.generateTreeTable)(constants_js_1.ANALYSIS_INFO_TITLE, this.config.properties);
|
|
37
|
+
this.config.ui.write("");
|
|
38
|
+
this.config.ui.write(output);
|
|
39
|
+
this.config.ui.write("");
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Validates that checker file exists
|
|
43
|
+
*/
|
|
44
|
+
validateCheckerFile() {
|
|
45
|
+
if (this.config.checkerPath != null &&
|
|
46
|
+
!(0, fs_1.existsSync)(this.config.checkerPath)) {
|
|
47
|
+
this.config.ui.write(`\n${constants_js_1.Sym.ERR} Checker file not found at ${this.config.checkerPath}`);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Compiles FunC checker to BoC and writes to temporary file
|
|
53
|
+
*/
|
|
54
|
+
async compileChecker(checkerFilename) {
|
|
55
|
+
if (this.config.checkerPath == null) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
this.config.ui.setActionPrompt(`${constants_js_1.Sym.WAIT} Compiling checker...`);
|
|
59
|
+
try {
|
|
60
|
+
const bocCode = await (0, build_utils_js_1.compileFuncFileToBase64Boc)(this.config.checkerPath, checkerFilename);
|
|
61
|
+
const bocBuffer = Buffer.from(bocCode, "base64");
|
|
62
|
+
this.tempBocPath = path_1.default.join((0, os_1.tmpdir)(), `checker-${Date.now()}.boc`);
|
|
63
|
+
(0, fs_1.writeFileSync)(this.tempBocPath, bocBuffer);
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
this.config.ui.clearActionPrompt();
|
|
67
|
+
this.config.ui.write(`\n${constants_js_1.Sym.ERR} Compilation failed: ${error.message}`);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Writes checker cell to temporary BoC file
|
|
73
|
+
*/
|
|
74
|
+
writeCheckerCell() {
|
|
75
|
+
const checkerCellBoc = this.config.checkerCell.toBoc();
|
|
76
|
+
this.tempCheckerCellPath = path_1.default.join((0, os_1.tmpdir)(), `checker-cell-${Date.now()}.boc`);
|
|
77
|
+
(0, fs_1.writeFileSync)(this.tempCheckerCellPath, checkerCellBoc);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Cleans up temporary files
|
|
81
|
+
*/
|
|
82
|
+
cleanup() {
|
|
83
|
+
if (this.tempBocPath && (0, fs_1.existsSync)(this.tempBocPath)) {
|
|
84
|
+
(0, fs_1.unlinkSync)(this.tempBocPath);
|
|
85
|
+
}
|
|
86
|
+
if (this.tempCheckerCellPath && (0, fs_1.existsSync)(this.tempCheckerCellPath)) {
|
|
87
|
+
(0, fs_1.unlinkSync)(this.tempCheckerCellPath);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Gets the temporary BoC file path (for use in analyzer arguments)
|
|
92
|
+
*/
|
|
93
|
+
getTempBocPath() {
|
|
94
|
+
if (!this.tempBocPath) {
|
|
95
|
+
throw new Error("Checker not compiled yet");
|
|
96
|
+
}
|
|
97
|
+
return this.tempBocPath;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Gets the temporary checker cell file path (for use in analyzer arguments)
|
|
101
|
+
*/
|
|
102
|
+
getTempCheckerCellPath() {
|
|
103
|
+
if (!this.tempCheckerCellPath) {
|
|
104
|
+
throw new Error("Checker cell not written yet");
|
|
105
|
+
}
|
|
106
|
+
return this.tempCheckerCellPath;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Runs the checker analysis with custom analyzer arguments
|
|
110
|
+
* @param checkerFilename - Name of the checker file to compile
|
|
111
|
+
* @param buildArgs - Callback function to build analyzer arguments after compilation
|
|
112
|
+
*/
|
|
113
|
+
async run(checkerFilename, buildArgs, completionMessage = "Analysis complete.") {
|
|
114
|
+
this.printAnalysisInfo();
|
|
115
|
+
this.validateCheckerFile();
|
|
116
|
+
if (checkerFilename != null) {
|
|
117
|
+
await this.compileChecker(checkerFilename);
|
|
118
|
+
}
|
|
119
|
+
this.writeCheckerCell();
|
|
120
|
+
try {
|
|
121
|
+
this.config.ui.clearActionPrompt();
|
|
122
|
+
this.config.ui.setActionPrompt(`${constants_js_1.Sym.WAIT} Running analysis...`);
|
|
123
|
+
const analyzerArgs = buildArgs(this);
|
|
124
|
+
const analyzer = await analyzer_js_1.Analyzer.create();
|
|
125
|
+
const logPath = (0, paths_js_1.getTsaRunLogPath)(this.id);
|
|
126
|
+
const result = await analyzer.run(analyzerArgs, logPath);
|
|
127
|
+
(0, fs_1.writeFileSync)(logPath, result.stdout);
|
|
128
|
+
this.config.ui.clearActionPrompt();
|
|
129
|
+
this.config.ui.write(`${constants_js_1.Sym.OK} ${completionMessage}`);
|
|
130
|
+
this.config.ui.write(`TSA run log available at: ${logPath}`);
|
|
131
|
+
if (result.stdout.trim().length > 0) {
|
|
132
|
+
this.config.ui.write(`${constants_js_1.Sym.WARN} Log file is not empty.`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
finally {
|
|
136
|
+
this.cleanup();
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
getVulnerability() {
|
|
140
|
+
const sarifPath = (0, paths_js_1.getSarifReportPath)(this.id);
|
|
141
|
+
const index = (0, result_parsing_js_1.findExploitExecutionIndex)(sarifPath);
|
|
142
|
+
if (index === undefined) {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
const dataPath = (0, paths_js_1.getContractDataBocPath)(this.id, index);
|
|
146
|
+
const value = (0, result_parsing_js_1.getMessageValue)(sarifPath, index);
|
|
147
|
+
const balance = (0, result_parsing_js_1.getInitialBalance)(sarifPath, index);
|
|
148
|
+
const msgBodyPath = (0, paths_js_1.getMsgBodyBocPath)(this.id, index);
|
|
149
|
+
const msgBodyBuffer = (0, fs_1.readFileSync)(msgBodyPath);
|
|
150
|
+
const msgBody = core_1.Cell.fromBoc(msgBodyBuffer)[0];
|
|
151
|
+
return {
|
|
152
|
+
value,
|
|
153
|
+
balance,
|
|
154
|
+
dataPath,
|
|
155
|
+
codePath: this.config.codePath,
|
|
156
|
+
executionIndex: index,
|
|
157
|
+
msgBody,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
vulnerabilityIsPresent() {
|
|
161
|
+
const sarifPath = (0, paths_js_1.getSarifReportPath)(this.id);
|
|
162
|
+
const index = (0, result_parsing_js_1.findExploitExecutionIndex)(sarifPath);
|
|
163
|
+
return index !== undefined;
|
|
164
|
+
}
|
|
165
|
+
reportVulnerability(vulnerability, descriptionUrl) {
|
|
166
|
+
const summaryPath = (0, paths_js_1.getSummaryPath)(this.id);
|
|
167
|
+
const sarifPath = (0, paths_js_1.getSarifReportPath)(this.id);
|
|
168
|
+
if (vulnerability == null) {
|
|
169
|
+
const report = `${constants_js_1.Sym.OK} Vulnerability not found.`;
|
|
170
|
+
(0, fs_1.writeFileSync)(summaryPath, report);
|
|
171
|
+
this.config.ui.write("");
|
|
172
|
+
this.config.ui.write(report);
|
|
173
|
+
this.config.ui.write("");
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const reportLines = [
|
|
177
|
+
`${constants_js_1.Sym.WARN} Vulnerability found!`,
|
|
178
|
+
`Summary path: ${summaryPath}`,
|
|
179
|
+
`Input message body and contract data: ${(0, paths_js_1.getInputsPath)(this.id, vulnerability.executionIndex)}`,
|
|
180
|
+
`Typed message body: ${(0, paths_js_1.getMsgBodyTypesPath)(this.id, vulnerability.executionIndex)}`,
|
|
181
|
+
`Typed contract data: ${(0, paths_js_1.getContractDataTypesPath)(this.id, vulnerability.executionIndex)}`,
|
|
182
|
+
`SARIF with full information: ${sarifPath}`,
|
|
183
|
+
];
|
|
184
|
+
if (descriptionUrl) {
|
|
185
|
+
reportLines.push("");
|
|
186
|
+
reportLines.push(`Description of the vulnerability: ${descriptionUrl}`);
|
|
187
|
+
}
|
|
188
|
+
reportLines.push("");
|
|
189
|
+
this.config.ui.write("");
|
|
190
|
+
for (const line of reportLines) {
|
|
191
|
+
this.config.ui.write(line);
|
|
192
|
+
}
|
|
193
|
+
const analysisInfo = (0, draw_js_1.generateTreeTable)(constants_js_1.ANALYSIS_INFO_TITLE, this.config.properties);
|
|
194
|
+
const report = analysisInfo + "\n\n" + reportLines.join("\n");
|
|
195
|
+
(0, fs_1.writeFileSync)(summaryPath, report);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
exports.AnalyzerWrapper = AnalyzerWrapper;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare class Analyzer {
|
|
2
|
+
javaPath: string;
|
|
3
|
+
tsaJarPath: string;
|
|
4
|
+
constructor(javaPath: string, tsaJarPath: string);
|
|
5
|
+
static create(): Promise<Analyzer>;
|
|
6
|
+
run(args: string[], logPath?: string): Promise<{
|
|
7
|
+
stdout: string;
|
|
8
|
+
stderr: string;
|
|
9
|
+
}>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Analyzer = void 0;
|
|
4
|
+
const java_js_1 = require("../install/java.js");
|
|
5
|
+
const tsa_jar_js_1 = require("../install/tsa-jar.js");
|
|
6
|
+
const child_process_1 = require("child_process");
|
|
7
|
+
const fs_1 = require("fs");
|
|
8
|
+
class Analyzer {
|
|
9
|
+
constructor(javaPath, tsaJarPath) {
|
|
10
|
+
this.javaPath = javaPath;
|
|
11
|
+
this.tsaJarPath = tsaJarPath;
|
|
12
|
+
}
|
|
13
|
+
static async create() {
|
|
14
|
+
const javaPath = await (0, java_js_1.ensureJavaInstalled)();
|
|
15
|
+
const tsaJarPath = await (0, tsa_jar_js_1.ensureTsaInstalled)();
|
|
16
|
+
return new Analyzer(javaPath, tsaJarPath);
|
|
17
|
+
}
|
|
18
|
+
run(args, logPath) {
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
const allArgs = ["-jar", this.tsaJarPath, ...args];
|
|
21
|
+
const proc = (0, child_process_1.spawn)(this.javaPath, allArgs);
|
|
22
|
+
let stdout = "";
|
|
23
|
+
let stderr = "";
|
|
24
|
+
proc.stdout?.on("data", (data) => {
|
|
25
|
+
const chunk = data.toString();
|
|
26
|
+
stdout += chunk;
|
|
27
|
+
});
|
|
28
|
+
proc.stderr?.on("data", (data) => {
|
|
29
|
+
const chunk = data.toString();
|
|
30
|
+
stderr += chunk;
|
|
31
|
+
});
|
|
32
|
+
proc.on("close", (code) => {
|
|
33
|
+
if (code === 0) {
|
|
34
|
+
resolve({ stdout, stderr });
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
console.error("\n" + stderr);
|
|
38
|
+
if (logPath) {
|
|
39
|
+
(0, fs_1.writeFileSync)(logPath, stdout);
|
|
40
|
+
console.error(`TSA log available at: ${logPath}`);
|
|
41
|
+
}
|
|
42
|
+
reject(new Error(`TSA process exited with code ${code}`));
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
proc.on("error", reject);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.Analyzer = Analyzer;
|
|
@@ -0,0 +1,68 @@
|
|
|
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.compileFuncFileToBase64Boc = exports.buildContracts = void 0;
|
|
7
|
+
const blueprint_1 = require("@ton/blueprint");
|
|
8
|
+
const fs_1 = require("fs");
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const func_js_1 = require("@ton-community/func-js");
|
|
11
|
+
const constants_js_1 = require("./constants.js");
|
|
12
|
+
const buildContracts = async (ui) => {
|
|
13
|
+
ui.setActionPrompt(`${constants_js_1.Sym.WAIT} Compiling contracts...`);
|
|
14
|
+
try {
|
|
15
|
+
await (0, blueprint_1.buildAll)(ui);
|
|
16
|
+
}
|
|
17
|
+
catch (e) {
|
|
18
|
+
ui.clearActionPrompt();
|
|
19
|
+
ui.write(e.toString());
|
|
20
|
+
ui.write(`\n${constants_js_1.Sym.ERR} Failed to compile one of the files`);
|
|
21
|
+
ui.write("Please make sure you can run `blueprint build --all` successfully before running TSA.");
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
ui.clearActionPrompt();
|
|
25
|
+
ui.write(`${constants_js_1.Sym.OK} Compiled.\n`);
|
|
26
|
+
};
|
|
27
|
+
exports.buildContracts = buildContracts;
|
|
28
|
+
const loadFuncSources = (filePath, baseDir, loaded = new Set()) => {
|
|
29
|
+
const sources = {};
|
|
30
|
+
const absolutePath = path_1.default.resolve(baseDir, filePath);
|
|
31
|
+
const normalizedPath = path_1.default.normalize(absolutePath);
|
|
32
|
+
if (loaded.has(normalizedPath)) {
|
|
33
|
+
return sources;
|
|
34
|
+
}
|
|
35
|
+
loaded.add(normalizedPath);
|
|
36
|
+
if (!(0, fs_1.existsSync)(absolutePath)) {
|
|
37
|
+
throw new Error(`File not found: ${absolutePath}`);
|
|
38
|
+
}
|
|
39
|
+
const content = (0, fs_1.readFileSync)(absolutePath, "utf-8");
|
|
40
|
+
const relativeKey = path_1.default
|
|
41
|
+
.relative(baseDir, absolutePath)
|
|
42
|
+
.split(path_1.default.sep)
|
|
43
|
+
.join("/");
|
|
44
|
+
sources[relativeKey] = content;
|
|
45
|
+
// Parse #include statements
|
|
46
|
+
const includeRegex = /#include\s+["<]([^"<>]+)["<]/g;
|
|
47
|
+
let match;
|
|
48
|
+
while ((match = includeRegex.exec(content)) !== null) {
|
|
49
|
+
const importPath = match[1];
|
|
50
|
+
const importDir = path_1.default.dirname(absolutePath);
|
|
51
|
+
const importedSources = loadFuncSources(importPath, importDir, loaded);
|
|
52
|
+
Object.assign(sources, importedSources);
|
|
53
|
+
}
|
|
54
|
+
return sources;
|
|
55
|
+
};
|
|
56
|
+
const compileFuncFileToBase64Boc = async (filePath, fileName) => {
|
|
57
|
+
const fileDir = path_1.default.dirname(filePath);
|
|
58
|
+
const sources = loadFuncSources(fileName, fileDir);
|
|
59
|
+
const compilationResult = await (0, func_js_1.compileFunc)({
|
|
60
|
+
targets: [fileName],
|
|
61
|
+
sources,
|
|
62
|
+
});
|
|
63
|
+
if (compilationResult.status === "error") {
|
|
64
|
+
throw new Error(`FunC compilation error: ${compilationResult.message}`);
|
|
65
|
+
}
|
|
66
|
+
return compilationResult.codeBoc;
|
|
67
|
+
};
|
|
68
|
+
exports.compileFuncFileToBase64Boc = compileFuncFileToBase64Boc;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export declare class Sym {
|
|
2
|
+
static OK: string;
|
|
3
|
+
static WARN: string;
|
|
4
|
+
static ERR: string;
|
|
5
|
+
static WAIT: string;
|
|
6
|
+
}
|
|
7
|
+
export declare const TSA_VERSION = "v0.5.0";
|
|
8
|
+
export declare const TSA_NAME = "tsa-cli-v0.5.0.jar";
|
|
9
|
+
export declare const TSA_URL = "https://github.com/espritoxyz/tsa/releases/download/v0.5.0/tsa-cli.jar";
|
|
10
|
+
export declare const DRAIN_CHECK_SYMBOLIC_FILENAME = "drain-check-symbolic.fc";
|
|
11
|
+
export declare const DRAIN_CHECK_CONCRETE_FILENAME = "drain-check-concrete.fc";
|
|
12
|
+
export declare const DRAIN_CHECK_ID = "drain-check";
|
|
13
|
+
export declare const DRAIN_DESCRIPTION_URL = "https://tonsec.dev/docs/builtin-checkers#drain-check";
|
|
14
|
+
export declare const REPRODUCE_ID = "reproduce";
|
|
15
|
+
export declare const REPLAY_ATTACK_CHECK_SYMBOLIC_FILENAME = "replay-attack-symbolic.fc";
|
|
16
|
+
export declare const REPLAY_ATTACK_CHECK_ID = "replay-attack-check";
|
|
17
|
+
export declare const REPLAY_DESCRIPTION_URL = "https://tonsec.dev/docs/builtin-checkers#replay-attack-checker";
|
|
18
|
+
export declare const BOUNCE_CHECK_FILENAME = "bounce-check.fc";
|
|
19
|
+
export declare const BOUNCE_CHECK_SCHEME_FILENAME = "bounce-check-scheme.json";
|
|
20
|
+
export declare const BOUNCE_CHECK_ID = "bounce-check";
|
|
21
|
+
export declare const BOUNCE_DESCRIPTION_URL = "https://tonsec.dev/docs/builtin-checkers#bounced-messages-processing-checker";
|
|
22
|
+
export declare const THROWER_FILENAME = "thrower-contract.fc";
|
|
23
|
+
export declare const OWNER_HIJACK_CHECK_ID = "owner-hijack-check";
|
|
24
|
+
export declare const OWNER_HIJACK_CHECK_SYMBOLIC_FILENAME = "owner-hijack-symbolic.fc";
|
|
25
|
+
export declare const OWNER_HIJACK_CHECK_CONCRETE_FILENAME = "owner-hijack-concrete.fc";
|
|
26
|
+
export declare const OWNER_HIJACK_DESCRIPTION_URL = "https://tonsec.dev/docs/builtin-checkers#ownership-hijack-check";
|
|
27
|
+
export declare const OPCODE_INFO = "opcode-info";
|
|
28
|
+
export declare const OPCODE_AUTHORIZATION_CHECK_FILENAME = "authorization-check.fc";
|
|
29
|
+
export declare const OPCODE_INFO_DESCRIPTION_URL = "https://tonsec.dev/docs/builtin-checkers#opcode-authorization-check";
|
|
30
|
+
export declare const AUDIT_ID = "audit";
|
|
31
|
+
export declare const DRAIN_CHECK_NAME = "Drain Check";
|
|
32
|
+
export declare const REPLAY_ATTACK_CHECK_NAME = "Replay Attack Check";
|
|
33
|
+
export declare const OWNER_HIJACK_CHECK_NAME = "Owner Hijack Check";
|
|
34
|
+
export declare const BOUNCE_CHECK_NAME = "Bounce Check";
|
|
35
|
+
export declare const ERROR_EXIT_CODE = 1000;
|
|
36
|
+
export declare const NON_FAILING_EXIT_CODE = 1001;
|
|
37
|
+
export declare const EXPECTED_MESSAGE_IN_SARIF = "TvmFailure(exit=TVM user defined error with exit code 1000, phase=TvmComputePhase)";
|
|
38
|
+
export declare const EXPECTED_MESSAGE_NON_FAILING = "TvmFailure(exit=TVM user defined error with exit code 1001, phase=TvmComputePhase)";
|
|
39
|
+
export declare const ANALYSIS_INFO_TITLE = "TSA analysis";
|
|
40
|
+
export declare const DEPLOY_AND_REPRODUCE_COMMAND = "deploy-and-reproduce";
|
|
41
|
+
export declare const DEPLOY_WAIT_ATTEMPTS = 30;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEPLOY_WAIT_ATTEMPTS = exports.DEPLOY_AND_REPRODUCE_COMMAND = exports.ANALYSIS_INFO_TITLE = exports.EXPECTED_MESSAGE_NON_FAILING = exports.EXPECTED_MESSAGE_IN_SARIF = exports.NON_FAILING_EXIT_CODE = exports.ERROR_EXIT_CODE = exports.BOUNCE_CHECK_NAME = exports.OWNER_HIJACK_CHECK_NAME = exports.REPLAY_ATTACK_CHECK_NAME = exports.DRAIN_CHECK_NAME = exports.AUDIT_ID = exports.OPCODE_INFO_DESCRIPTION_URL = exports.OPCODE_AUTHORIZATION_CHECK_FILENAME = exports.OPCODE_INFO = exports.OWNER_HIJACK_DESCRIPTION_URL = exports.OWNER_HIJACK_CHECK_CONCRETE_FILENAME = exports.OWNER_HIJACK_CHECK_SYMBOLIC_FILENAME = exports.OWNER_HIJACK_CHECK_ID = exports.THROWER_FILENAME = exports.BOUNCE_DESCRIPTION_URL = exports.BOUNCE_CHECK_ID = exports.BOUNCE_CHECK_SCHEME_FILENAME = exports.BOUNCE_CHECK_FILENAME = exports.REPLAY_DESCRIPTION_URL = exports.REPLAY_ATTACK_CHECK_ID = exports.REPLAY_ATTACK_CHECK_SYMBOLIC_FILENAME = exports.REPRODUCE_ID = exports.DRAIN_DESCRIPTION_URL = exports.DRAIN_CHECK_ID = exports.DRAIN_CHECK_CONCRETE_FILENAME = exports.DRAIN_CHECK_SYMBOLIC_FILENAME = exports.TSA_URL = exports.TSA_NAME = exports.TSA_VERSION = exports.Sym = void 0;
|
|
4
|
+
class Sym {
|
|
5
|
+
}
|
|
6
|
+
exports.Sym = Sym;
|
|
7
|
+
Sym.OK = "✅";
|
|
8
|
+
Sym.WARN = "⚠️";
|
|
9
|
+
Sym.ERR = "❌";
|
|
10
|
+
Sym.WAIT = "⏳";
|
|
11
|
+
exports.TSA_VERSION = "v0.5.0";
|
|
12
|
+
exports.TSA_NAME = `tsa-cli-${exports.TSA_VERSION}.jar`;
|
|
13
|
+
exports.TSA_URL = `https://github.com/espritoxyz/tsa/releases/download/${exports.TSA_VERSION}/tsa-cli.jar`;
|
|
14
|
+
exports.DRAIN_CHECK_SYMBOLIC_FILENAME = "drain-check-symbolic.fc";
|
|
15
|
+
exports.DRAIN_CHECK_CONCRETE_FILENAME = "drain-check-concrete.fc";
|
|
16
|
+
exports.DRAIN_CHECK_ID = "drain-check";
|
|
17
|
+
exports.DRAIN_DESCRIPTION_URL = "https://tonsec.dev/docs/builtin-checkers#drain-check";
|
|
18
|
+
exports.REPRODUCE_ID = "reproduce";
|
|
19
|
+
exports.REPLAY_ATTACK_CHECK_SYMBOLIC_FILENAME = "replay-attack-symbolic.fc";
|
|
20
|
+
exports.REPLAY_ATTACK_CHECK_ID = "replay-attack-check";
|
|
21
|
+
exports.REPLAY_DESCRIPTION_URL = "https://tonsec.dev/docs/builtin-checkers#replay-attack-checker";
|
|
22
|
+
exports.BOUNCE_CHECK_FILENAME = "bounce-check.fc";
|
|
23
|
+
exports.BOUNCE_CHECK_SCHEME_FILENAME = "bounce-check-scheme.json";
|
|
24
|
+
exports.BOUNCE_CHECK_ID = "bounce-check";
|
|
25
|
+
exports.BOUNCE_DESCRIPTION_URL = "https://tonsec.dev/docs/builtin-checkers#bounced-messages-processing-checker";
|
|
26
|
+
exports.THROWER_FILENAME = "thrower-contract.fc";
|
|
27
|
+
exports.OWNER_HIJACK_CHECK_ID = "owner-hijack-check";
|
|
28
|
+
exports.OWNER_HIJACK_CHECK_SYMBOLIC_FILENAME = "owner-hijack-symbolic.fc";
|
|
29
|
+
exports.OWNER_HIJACK_CHECK_CONCRETE_FILENAME = "owner-hijack-concrete.fc";
|
|
30
|
+
exports.OWNER_HIJACK_DESCRIPTION_URL = "https://tonsec.dev/docs/builtin-checkers#ownership-hijack-check";
|
|
31
|
+
exports.OPCODE_INFO = "opcode-info";
|
|
32
|
+
exports.OPCODE_AUTHORIZATION_CHECK_FILENAME = "authorization-check.fc";
|
|
33
|
+
exports.OPCODE_INFO_DESCRIPTION_URL = "https://tonsec.dev/docs/builtin-checkers#opcode-authorization-check";
|
|
34
|
+
exports.AUDIT_ID = "audit";
|
|
35
|
+
exports.DRAIN_CHECK_NAME = "Drain Check";
|
|
36
|
+
exports.REPLAY_ATTACK_CHECK_NAME = "Replay Attack Check";
|
|
37
|
+
exports.OWNER_HIJACK_CHECK_NAME = "Owner Hijack Check";
|
|
38
|
+
exports.BOUNCE_CHECK_NAME = "Bounce Check";
|
|
39
|
+
exports.ERROR_EXIT_CODE = 1000;
|
|
40
|
+
exports.NON_FAILING_EXIT_CODE = 1001;
|
|
41
|
+
exports.EXPECTED_MESSAGE_IN_SARIF = `TvmFailure(exit=TVM user defined error with exit code ${exports.ERROR_EXIT_CODE}, phase=TvmComputePhase)`;
|
|
42
|
+
exports.EXPECTED_MESSAGE_NON_FAILING = `TvmFailure(exit=TVM user defined error with exit code ${exports.NON_FAILING_EXIT_CODE}, phase=TvmComputePhase)`;
|
|
43
|
+
exports.ANALYSIS_INFO_TITLE = "TSA analysis";
|
|
44
|
+
exports.DEPLOY_AND_REPRODUCE_COMMAND = "deploy-and-reproduce";
|
|
45
|
+
exports.DEPLOY_WAIT_ATTEMPTS = 30;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateTreeTable = generateTreeTable;
|
|
4
|
+
function generateTreeTable(title, properties) {
|
|
5
|
+
const lines = [];
|
|
6
|
+
const titleLine = `╭─${title}─╮`;
|
|
7
|
+
const headerLine = `╰─┬${"─".repeat(titleLine.length - 4)}╯`;
|
|
8
|
+
lines.push(titleLine);
|
|
9
|
+
lines.push(headerLine);
|
|
10
|
+
properties.forEach((prop, index) => {
|
|
11
|
+
if (prop.separator) {
|
|
12
|
+
lines.push(" │");
|
|
13
|
+
}
|
|
14
|
+
const isLast = index === properties.length - 1;
|
|
15
|
+
const prefix = isLast ? " └──" : " ├──";
|
|
16
|
+
const line = prop.value
|
|
17
|
+
? `${prefix} ${prop.key}: ${prop.value}`
|
|
18
|
+
: `${prefix} ${prop.key}`;
|
|
19
|
+
lines.push(line);
|
|
20
|
+
if (prop.children && prop.children.length > 0) {
|
|
21
|
+
const childPrefix = isLast ? " " : " │ ";
|
|
22
|
+
prop.children.forEach((child, childIndex) => {
|
|
23
|
+
const isLastChild = childIndex === prop.children.length - 1;
|
|
24
|
+
const childConnector = isLastChild ? "└──" : "├──";
|
|
25
|
+
const childLine = child.value
|
|
26
|
+
? `${childConnector} ${child.key}: ${child.value}`
|
|
27
|
+
: `${childConnector} ${child.key}`;
|
|
28
|
+
lines.push(`${childPrefix}${childLine}`);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
return lines.join("\n");
|
|
33
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Executes a function with a temporary file, ensuring cleanup in all cases
|
|
3
|
+
* @param callback - Function that receives the temporary file path and returns a promise
|
|
4
|
+
* @param tempFilePath - Path to the temporary file to create and clean up
|
|
5
|
+
* @returns The result of the callback function
|
|
6
|
+
*/
|
|
7
|
+
export declare function doWithTemporaryFile<T>(callback: (tempFilePath: string) => Promise<T>, tempFilePath: string): Promise<T>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.doWithTemporaryFile = doWithTemporaryFile;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
/**
|
|
6
|
+
* Executes a function with a temporary file, ensuring cleanup in all cases
|
|
7
|
+
* @param callback - Function that receives the temporary file path and returns a promise
|
|
8
|
+
* @param tempFilePath - Path to the temporary file to create and clean up
|
|
9
|
+
* @returns The result of the callback function
|
|
10
|
+
*/
|
|
11
|
+
async function doWithTemporaryFile(callback, tempFilePath) {
|
|
12
|
+
try {
|
|
13
|
+
return await callback(tempFilePath);
|
|
14
|
+
}
|
|
15
|
+
finally {
|
|
16
|
+
if ((0, fs_1.existsSync)(tempFilePath)) {
|
|
17
|
+
(0, fs_1.unlinkSync)(tempFilePath);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formats an opcode as an 8-digit hexadecimal string with 0x prefix
|
|
3
|
+
* @param opcode - The opcode number to format
|
|
4
|
+
* @returns Formatted opcode string (e.g., "0x00000001")
|
|
5
|
+
*/
|
|
6
|
+
export declare function formatOpcodeHex(opcode: number): string;
|
|
7
|
+
/**
|
|
8
|
+
* Generates a unique identifier based on current date and time
|
|
9
|
+
* Format: YYYY-MM-DD-milliseconds (where milliseconds is time since start of day)
|
|
10
|
+
* @returns Unique identifier string
|
|
11
|
+
*/
|
|
12
|
+
export declare function generateReportId(): string;
|
|
13
|
+
export declare function nanotonToTon(nanotons: bigint): number;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatOpcodeHex = formatOpcodeHex;
|
|
4
|
+
exports.generateReportId = generateReportId;
|
|
5
|
+
exports.nanotonToTon = nanotonToTon;
|
|
6
|
+
/**
|
|
7
|
+
* Formats an opcode as an 8-digit hexadecimal string with 0x prefix
|
|
8
|
+
* @param opcode - The opcode number to format
|
|
9
|
+
* @returns Formatted opcode string (e.g., "0x00000001")
|
|
10
|
+
*/
|
|
11
|
+
function formatOpcodeHex(opcode) {
|
|
12
|
+
return `0x${opcode.toString(16).padStart(8, "0")}`;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Generates a unique identifier based on current date and time
|
|
16
|
+
* Format: YYYY-MM-DD-milliseconds (where milliseconds is time since start of day)
|
|
17
|
+
* @returns Unique identifier string
|
|
18
|
+
*/
|
|
19
|
+
function generateReportId() {
|
|
20
|
+
const now = new Date();
|
|
21
|
+
const year = now.getFullYear();
|
|
22
|
+
const month = String(now.getMonth() + 1).padStart(2, "0");
|
|
23
|
+
const day = String(now.getDate()).padStart(2, "0");
|
|
24
|
+
const startOfDay = new Date(year, now.getMonth(), now.getDate());
|
|
25
|
+
const milliseconds = now.getTime() - startOfDay.getTime();
|
|
26
|
+
return `${year}-${month}-${day}-${milliseconds}`;
|
|
27
|
+
}
|
|
28
|
+
function nanotonToTon(nanotons) {
|
|
29
|
+
return Number(nanotons) / 1e9;
|
|
30
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractOpcodes = extractOpcodes;
|
|
4
|
+
const core_1 = require("@ton/core");
|
|
5
|
+
const fs_1 = require("fs");
|
|
6
|
+
const os_1 = require("os");
|
|
7
|
+
const path_1 = require("path");
|
|
8
|
+
const crypto_1 = require("crypto");
|
|
9
|
+
const analyzer_wrapper_js_1 = require("./analyzer-wrapper.js");
|
|
10
|
+
const format_utils_js_1 = require("./format-utils.js");
|
|
11
|
+
const file_utils_js_1 = require("./file-utils.js");
|
|
12
|
+
const OPCODE_EXTRACTION_TIMEOUT_SECONDS = 20;
|
|
13
|
+
async function extractOpcodes(config) {
|
|
14
|
+
config.ui.write("");
|
|
15
|
+
config.ui.write("Extracting contract opcodes for better path selection strategy...");
|
|
16
|
+
const properties = [
|
|
17
|
+
{ key: "Contract", value: config.contractName },
|
|
18
|
+
{ key: "Mode", value: "Opcode extraction" },
|
|
19
|
+
{
|
|
20
|
+
key: "Options",
|
|
21
|
+
separator: true,
|
|
22
|
+
children: [
|
|
23
|
+
{
|
|
24
|
+
key: "Timeout",
|
|
25
|
+
value: `${OPCODE_EXTRACTION_TIMEOUT_SECONDS} seconds`,
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
];
|
|
30
|
+
// Create a temporary file for opcode output
|
|
31
|
+
const outputFile = (0, path_1.join)((0, os_1.tmpdir)(), `opcodes-${(0, crypto_1.randomBytes)(8).toString("hex")}.txt`);
|
|
32
|
+
return (0, file_utils_js_1.doWithTemporaryFile)(async (tempPath) => {
|
|
33
|
+
const analyzer = new analyzer_wrapper_js_1.AnalyzerWrapper({
|
|
34
|
+
ui: config.ui,
|
|
35
|
+
checkerPath: null,
|
|
36
|
+
checkerCell: new core_1.Cell(),
|
|
37
|
+
properties,
|
|
38
|
+
codePath: config.codePath,
|
|
39
|
+
});
|
|
40
|
+
const args = [
|
|
41
|
+
"opcodes",
|
|
42
|
+
"--input",
|
|
43
|
+
config.codePath,
|
|
44
|
+
"--output",
|
|
45
|
+
tempPath,
|
|
46
|
+
"--timeout",
|
|
47
|
+
OPCODE_EXTRACTION_TIMEOUT_SECONDS.toString(),
|
|
48
|
+
];
|
|
49
|
+
await analyzer.run(null, () => args, "Opcode extraction completed.");
|
|
50
|
+
// Read and parse the output file
|
|
51
|
+
const content = (0, fs_1.readFileSync)(tempPath, "utf-8").trim();
|
|
52
|
+
if (content.length === 0) {
|
|
53
|
+
config.ui.write("Extracted opcodes: []");
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
const opcodes = content.split("\n").map((op) => parseInt(op.trim(), 10));
|
|
57
|
+
config.ui.write(`Extracted opcodes: [${opcodes.map((op) => (0, format_utils_js_1.formatOpcodeHex)(op)).join(", ")}]`);
|
|
58
|
+
return opcodes;
|
|
59
|
+
}, outputFile);
|
|
60
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare const BUILD_DIR: string;
|
|
2
|
+
export declare const findTSAHomeDirectory: () => string;
|
|
3
|
+
export declare const findTSAProjectDirectory: () => string;
|
|
4
|
+
export declare const findTSAReportsDirectory: () => string;
|
|
5
|
+
export declare const getReportDirectory: (id: string) => string;
|
|
6
|
+
export declare const findCompiledContract: (name: string) => string;
|
|
7
|
+
export declare const findJavaBinPath: () => string | null;
|
|
8
|
+
export declare const findTsaPath: () => string | null;
|
|
9
|
+
export declare const getCheckerPath: (checkerName: string) => string;
|
|
10
|
+
export declare const getSarifReportPath: (id: string) => string;
|
|
11
|
+
export declare const getSummaryPath: (id: string) => string;
|
|
12
|
+
export declare const getInputsPath: (id: string, index: number) => string;
|
|
13
|
+
export declare const getReproduceConfigPath: (id: string) => string;
|
|
14
|
+
export declare const getContractDataBocPath: (id: string, index: number) => string;
|
|
15
|
+
export declare const getContractDataTypesPath: (id: string, index: number) => string;
|
|
16
|
+
export declare const getMsgBodyBocPath: (id: string, index: number) => string;
|
|
17
|
+
export declare const getMsgBodyTypesPath: (id: string, index: number) => string;
|
|
18
|
+
export declare const getTsaRunLogPath: (id: string) => string;
|
|
19
|
+
export declare const getThrowerPath: () => string;
|