Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Audit tool now exits with nonzero error code if severe issues are found #29

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
183 changes: 182 additions & 1 deletion src/main/java/org/web3j/console/ContractAuditor.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,202 @@
*/
package org.web3j.console;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.FileSystemAlreadyExistsException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPathFactory;

import ru.smartdec.smartcheck.RulesCached;
import ru.smartdec.smartcheck.RulesXml;
import ru.smartdec.smartcheck.app.DirectoryAnalysis;
import ru.smartdec.smartcheck.app.DirectoryAnalysisCombined;
import ru.smartdec.smartcheck.app.DirectoryAnalysisDefault;
import ru.smartdec.smartcheck.app.Media;
import ru.smartdec.smartcheck.app.ReportDefault;
import ru.smartdec.smartcheck.app.SourceLanguage;
import ru.smartdec.smartcheck.app.SourceLanguages;
import ru.smartdec.smartcheck.app.TreeFactoryDefault;
import ru.smartdec.smartcheck.app.cli.Tool;

import static org.web3j.codegen.Console.exitError;

public class ContractAuditor {

private static final String USAGE = "audit <file name>";

private static DirectoryAnalysis makeDirectoryAnalysis(
final SourceLanguage sourceLanguage,
final Path source,
final Function<SourceLanguage, RulesXml.Source> rules)
throws Exception {
return new DirectoryAnalysisDefault(
source,
p -> p.toString().endsWith(sourceLanguage.fileExtension()),
new TreeFactoryDefault(
DocumentBuilderFactory.newInstance().newDocumentBuilder(), sourceLanguage),
new RulesCached(
new RulesXml(
rules.apply(sourceLanguage),
XPathFactory.newInstance().newXPath(),
Throwable::printStackTrace)));
}

public static void main(String[] args) {
if (args.length != 1) {
exitError(USAGE);
}
try {
ru.smartdec.smartcheck.app.cli.Tool.main(new String[] {"-p", args[0]});
Path source = Paths.get(args[0]);
Function<SourceLanguage, RulesXml.Source> defaultRules =
sourceLanguage ->
() -> {
String rulesFileName = sourceLanguage.rulesFileName();
URI uri = RulesXml.class.getResource(rulesFileName).toURI();
try {
HashMap<String, String> env = new HashMap<>();
env.put("create", "true");
FileSystems.newFileSystem(uri, env);
} catch (FileSystemAlreadyExistsException ignored) {
}
return Paths.get(uri);
};

final Integer[] totals = {0, 0};
DefaultMedia media = new DefaultMedia(totals);
new ReportDefault(
new DirectoryAnalysisCombined(
makeDirectoryAnalysis(
new SourceLanguages.Solidity(), source, defaultRules),
makeDirectoryAnalysis(
new SourceLanguages.Vyper(), source, defaultRules)),
media)
.print();

if (media.getTotals()[1] > 0) {
System.exit(-1);
}
} catch (Exception e) {
System.err.println("The audit operation failed with the following exception:");
e.printStackTrace();
}
}
}

class DefaultMedia implements Media {

Integer[] getTotals() {
return totals;
}

private final Integer[] totals;

DefaultMedia(final Integer[] totals) {
this.totals = totals;
}

@Override
public void accept(final DirectoryAnalysis.Info info) {
LinkedList<List<String>> report_fields = new LinkedList<>();
Map<String, Integer> result = new HashMap<>();
info.treeReport()
.streamUnchecked()
.forEach(
tree ->
tree.contexts()
.forEach(
context -> {
LinkedList<String> fields = new LinkedList<>();
String rule_name;
try {
URL rule_name_resource =
Tool.class
.getClassLoader()
.getResource(
String.format(
"rule_descriptions/%s/name_en.txt",
tree.rule()
.id()));
if (rule_name_resource != null) {
rule_name =
new String(
Files.readAllBytes(
Paths.get(
rule_name_resource
.toURI())));
} else {
rule_name = "";
}
} catch (IOException | URISyntaxException e) {
rule_name = "";
}
fields.addLast("");
fields.addLast(
String.format(
"%d:%d",
context.getStart().getLine(),
context.getStart()
.getCharPositionInLine()));
fields.addLast(
String.format(
"severity:%d",
tree.pattern().severity()));
if (tree.pattern().severity() > 1) {
totals[1]++;
}
fields.addLast(rule_name);
fields.addLast(
String.format(
"%s_%s",
tree.rule().id(),
tree.pattern().id()));
result.compute(
tree.rule().id(),
(k, v) ->
Optional.ofNullable(v)
.map(i -> i + 1)
.orElse(1));
report_fields.addLast(fields);
}));
if (!report_fields.isEmpty()) {
System.out.println(info.file());
System.out.print(formatAsTable(report_fields));
totals[0] += report_fields.size();
}
}

private static String formatAsTable(List<List<String>> rows) {
if (rows.isEmpty()) return "";
int[] maxLengths = new int[rows.get(0).size()];
for (List<String> row : rows) {
for (int i = 0; i < row.size(); i++) {
maxLengths[i] = Math.max(maxLengths[i], row.get(i).length());
}
}

StringBuilder formatBuilder = new StringBuilder();
for (int maxLength : maxLengths) {
formatBuilder.append("%-").append(maxLength + 3).append("s");
}
String format = formatBuilder.toString();

StringBuilder result = new StringBuilder();
for (List<String> row : rows) {
String[] res = row.toArray(new String[0]);
result.append(String.format(format, (Object[]) res)).append("\n");
}
return result.toString();
}
}