Exemple #1
0
def analyze(path: Path):
    cfg = compile_cfg(str(path)).cfg
    result = static_analysis.analyze_cfg(cfg).facts_out

    patterns = result["patternId"]
    pattern_names = result["patternName"]
    pattern_descriptions = result["patternDescription"]

    pattern_names = dict(pattern_names)
    pattern_descriptions = dict(pattern_descriptions)

    compliant = result["patternCompliance"]
    violation = result["patternViolation"]

    conflict = result["patternConflict"]
    warnings = result["patternWarning"]

    def grouped(collection, key):
        return {k: list(v) for k, v in groupby(collection, key)}

    def group_matches(matches):
        return {
            pattern: grouped(match_list, lambda t: t[1])
            for pattern, match_list in grouped(matches,
                                               lambda t: t[0]).items()
        }

    # compliant =

    compliant = group_matches(compliant)
    violation = group_matches(violation)
    warnings = group_matches(warnings)
    conflict = group_matches(conflict)

    patterns = [
        PatternInfo(pattern, pattern_names.get(pattern),
                    pattern_descriptions.get(pattern))
        for pattern, *_ in patterns
    ]

    # noinspection PyTypeChecker
    matches = {
        pattern: [
            Compliance(match[0][1], match[0][2],
                       [m[3] if len(m) > 3 else "" for m in match])
            for match in compliant.get(pattern.id, {}).values()
        ] + [
            Violation(match[0][1], match[0][2], [m[3] for m in match])
            for match in violation.get(pattern.id, {}).values()
        ] + [
            Warning(match[0][1], "N/A")
            for match in warnings.get(pattern.id, {}).values()
        ] + [
            Conflict(match[0][1], "N/A", [], [])
            for match in conflict.get(pattern.id, {}).values()
        ]
        for pattern in patterns
    }

    return AnalysisResults(patterns, matches, result)
Exemple #2
0
def main():

    args = parse_arguments()

    prepare_env(binary=args.solidity)

    sys.setrecursionlimit(args.stack_limit)

    contract = args.contract

    if args.from_blockchain:
        contract = get_contract_from_blockchain(args.contract, args.key)

    if not args.ignore_pragma:
        contract = fix_pragma(contract)

    souffle_config = dict(use_interpreter=args.interpreter,
                          force_recompilation=args.recompile,
                          library_dir=args.library_dir)

    config = AnalysisConfiguration(
        # TODO: this returns only the dict ast, but should return the object representation
        ast_compiler=lambda t: solidity_ast_compiler.compile_ast(t.source_file
                                                                 ),
        cfg_compiler=lambda t: solidity_cfg_compiler.compile_cfg(t.ast).cfg,
        static_analysis=lambda t: static_analysis.analyze_cfg(
            t.cfg, **souffle_config),
    )

    context = AnalysisContext(config=config, source_file=contract)

    if args.visualize:
        cfg = context.cfg
        facts, _ = encode(cfg)
        visualize(facts).render("out/dl", format="svg", cleanup=True)

    patterns = get_list_of_patterns(context=context,
                                    patterns=args.use_patterns,
                                    exclude_patterns=args.exclude_patterns,
                                    severity_inc=args.include_severity,
                                    severity_exc=args.exclude_severity)

    matches = []

    for pattern in patterns:
        matches.extend(pattern.find_matches())

    skip_compliant = not args.show_compliants
    print_pattern_matches(context,
                          matches,
                          skip_compliant=skip_compliant,
                          include_contracts=args.include_contracts,
                          exclude_contracts=args.exclude_contracts)
Exemple #3
0
    def test_case(self: unittest.TestCase):
        # if USE_COMPILATION_CACHE:
        #     cfg, ast, *_ = compile_cached(path_src)
        # else:
        #path_src = fix_pragma(path_src)
        new_src = fix_pragma(path_src)

        cfg, ast, *_ = compile_cfg(new_src)

        result = static_analysis.analyze_cfg(cfg, logger=logger).facts_out

        with open(path_src, 'r') as f:
            src_lines = f.readlines()

        lines = dropwhile(lambda l: "/**" not in l, src_lines)
        lines = takewhile(lambda l: "*/" not in l, lines)
        lines = list(lines)[1:]

        config = configparser.ConfigParser()
        config.read_string("".join(lines))

        specs = config["Specs"]
        pattern = specs["pattern"]

        compliant = [c.strip() for c in specs.get("compliant", "").split(",")]
        violation = [c.strip() for c in specs.get("violation", "").split(",")]

        compliant += [f"L{i + 1}" for i, l in enumerate(src_lines) if
                      "//" in l and "compliant" in l.split("//")[1].lower()]

        violation += [f"L{i + 1}" for i, l in enumerate(src_lines) if
                      "//" in l and "violation" in l.split("//")[1].lower()]

        compliant = [s.strip() for s in compliant if "" != s.strip()]
        violation = [s.strip() for s in violation if "" != s.strip()]

        pattern_matches = [t[1:] for t in result["patternMatch"] if t[0] == pattern]
        pattern_matches_lines = [t[1:] for t in result["patternMatchInfo"] if t[0] == pattern]
        pattern_matches_lines = {match: line for match, key, line in pattern_matches_lines if key == "line"}

        compliant_output = [pattern_matches_lines[m] for m, c in pattern_matches if c == "compliant"]
        violation_output = [pattern_matches_lines[m] for m, c in pattern_matches if c == "violation"]
        conflict_output = [pattern_matches_lines[m] for m, c in pattern_matches if c == "conflict"]

        def compare(expected, actual, e):
            try:
                self.assertSetEqual(set(expected), set(actual), e)
            except AssertionError as e:  # Fix ambiguous error messages
                msg = e.args[0]
                msg = msg.replace("Items in the first set but not the second",
                                  "Items expected but not reported")

                msg = msg.replace("Items in the second set but not the first",
                                  "Items incorrectly reported")

                raise AssertionError(msg) from None

        if conflict_output:
            data = pprint.pformat(conflict_output)
            raise Exception("Conflict\n" + data)

        compare(compliant, compliant_output, "Compliance")
        compare(violation, violation_output, "Violations")
Exemple #4
0
    def test_case(self: unittest.TestCase):
        config = AnalysisConfiguration(
            # TODO: this returns only the dict ast, but should return the object representation
            ast_compiler=lambda t: solidity_ast_compiler.compile_ast(
                t.source_file),
            cfg_compiler=lambda t: solidity_cfg_compiler.compile_cfg(t.ast).
            cfg,
            static_analysis=lambda t: static_analysis.analyze_cfg(t.cfg),
        )

        context = AnalysisContext(config=config, source_file=path_src)

        test_info, annotations = read_test_info()

        patterns = TestPatterns.patterns
        patterns = [p for p in patterns if p.__name__ == test_info["pattern"]]

        assert len(patterns) == 1, len(patterns)

        matches: List[PatternMatch] = []

        for pattern in patterns:
            matches += pattern(context).find_matches()

        self.assertFalse(
            [m for m in matches if not isinstance(m, PatternMatch)],
            "Result must be a PatternMatch")
        self.assertFalse([m for m in matches if m.type == MatchType.CONFLICT],
                         "Conflicts must not happen")

        compliant = [
            next(m.find_info(MatchSourceLocation)).line for m in matches
            if m.type == MatchType.COMPLIANT
        ]
        violation = [
            next(m.find_info(MatchSourceLocation)).line for m in matches
            if m.type == MatchType.VIOLATION
        ]
        warning = [
            next(m.find_info(MatchSourceLocation)).line for m in matches
            if m.type == MatchType.WARNING
        ]

        def compare(expected, actual, e):
            try:
                self.assertSetEqual(set(expected), set(actual), e)
            except AssertionError as e:  # Fix ambiguous error messages
                msg = e.args[0]
                msg = msg.replace("Items in the first set but not the second",
                                  "Items expected but not reported")

                msg = msg.replace("Items in the second set but not the first",
                                  "Items incorrectly reported")

                raise AssertionError(msg) from None

        compare([t[0] for t in annotations["compliant"]], compliant,
                "Compliant")
        compare([t[0] for t in annotations["violation"]], violation,
                "Violation")
        compare([t[0] for t in annotations["warning"]], warning, "Warnings")
Exemple #5
0
def main():

    save_stdout = sys.stdout

    args = parse_arguments()

    # suppress the output when outputting json
    if args.output_json:
        sys.stdout = open('/tmp/securify_suppressed.out', 'w')

    prepare_env(binary=args.solidity)

    sys.setrecursionlimit(args.stack_limit)

    contract = args.contract

    if args.flatten:
        my_module = importlib.import_module("securify.utils.flattener",
                                            package=".")
        contract = my_module.flatten(contract)

    if args.from_blockchain:
        contract = get_contract_from_blockchain(args.contract, args.key)

    if not args.ignore_pragma:
        contract = fix_pragma(contract)

    souffle_config = dict(use_interpreter=args.interpreter,
                          force_recompilation=args.recompile,
                          library_dir=args.library_dir)

    config = AnalysisConfiguration(
        # TODO: this returns only the dict ast, but should return the object representation
        ast_compiler=lambda t: solidity_ast_compiler.compile_ast(t.source_file
                                                                 ),
        cfg_compiler=lambda t: solidity_cfg_compiler.compile_cfg(t.ast).cfg,
        static_analysis=lambda t: static_analysis.analyze_cfg(
            t.cfg, **souffle_config),
    )

    context = AnalysisContext(config=config, source_file=contract)

    if args.visualize:
        cfg = context.cfg
        facts, _ = encode(cfg)
        visualize(facts).render("out/dl", format="svg", cleanup=True)

    patterns = get_list_of_patterns(context=context,
                                    patterns=args.use_patterns,
                                    exclude_patterns=args.exclude_patterns,
                                    severity_inc=args.include_severity,
                                    severity_exc=args.exclude_severity)

    matches = []

    for pattern in patterns:
        matches.extend(pattern.find_matches())

    skip_compliant = not args.show_compliants

    sys.stdout = save_stdout
    if args.output_json:
        print_pattern_matches_json(context,
                                   matches,
                                   skip_compliant=skip_compliant,
                                   include_contracts=args.include_contracts,
                                   exclude_contracts=args.exclude_contracts)
    else:
        print_pattern_matches(context,
                              matches,
                              skip_compliant=skip_compliant,
                              include_contracts=args.include_contracts,
                              exclude_contracts=args.exclude_contracts)
Exemple #6
0
    #for comment in match.find_info(MatchComment):
    #     lines.append(textwrap.fill(comment.comment,
    #                                initial_indent="Comment: ",
    #                                subsequent_indent="         "))
    lines.append("\n")

    return "\n".join(lines)


if __name__ == '__main__':
    config = AnalysisConfiguration(
        # TODO: this returns only the dict ast, but should return the object representation
        ast_compiler=lambda t: solidity_ast_compiler.compile_ast(t.source_file),
        cfg_compiler=lambda t: solidity_cfg_compiler.compile_cfg(t.ast).cfg,
        static_analysis=lambda t: static_analysis.analyze_cfg(t.cfg),
    )

    context = AnalysisContext(
        config=config,
        source_file="testContract.sol"
    )

    patterns = discover_patterns()

    matches = []
    for pattern in patterns:
        matches.extend(pattern(context).find_matches())

    print_pattern_matches(context, matches)
Exemple #7
0
            g.edge(next.strip(), prev.strip())
        else:
            g.edge(prev.strip(), next.strip())

    g.render("out/" + name, format="png", cleanup=True)


if __name__ == '__main__':
    cfg = compile_cfg("testContract.sol").cfg
    # visualizer.draw_cfg(cfg, file='out/cfg', format='png', only_blocks=True, view=False)

    result = analyze_cfg(
        cfg=cfg,
        logger=print,
        **{
            "library_dir": Path(__file__).parent / "libfunctors",
            "profile_out": "souffle-profile.json",
            # "profile_use": "souffle-profile.json",
            # "report_out": "souffle-report.html"
        })

    visualize(result.facts).render("out/dl", format="svg", cleanup=True)

    # print(result.stderr)
    # print(result.stdout)

    # graph(facts_out["programFlow.followsInBlockWithContextToString"], "wtf3")
    # graph(facts_out["followsWithContextToString"], "wtf2")
    # graph(facts_out["followsBlockWithContextToString"], "wtf")
    # graph(facts_out["programFlow.mustPrecedeToString"], "dom", "circo", inv=True)
Exemple #8
0
def compare(path_src):
    securify_start_time = time.time()
    cfg, ast, *_ = compile_cfg(path_src)
    result = static_analysis.analyze_cfg(cfg).facts_out
    securify_end_time = time.time()

    def group_matches(results):
        return {k: list(v) for k, v in groupby(results, lambda t: t[0])}

    compliant_securify = group_matches(result["patternCompliance"])
    violation_securify = group_matches(result["patternViolation"])
    warnings_securify = group_matches(result["patternWarning"])
    conflict_securify = group_matches(result["patternConflict"])

    secu_start_time = time.time()
    securify_output = securify_wrapper.run_securify(path_src)
    secu_end_time = time.time()

    # print("")
    # print("Securify")
    # print("=========")
    # print("Compliant ")
    # pprint(list(compliant_securify.values()))
    # print("Violation ")
    # pprint(list(violation_securify.values()))
    # print("Warnings ")
    # pprint(list(warnings_securify.values()))
    # print("Conflict ")
    # pprint(list(conflict_securify.values()))
    #
    # print("")
    # print("Securify")
    # print("=========")
    # pprint(securify_output)

    # print(secu_end_time - secu_start_time)
    # print(securify_end_time - securify_start_time)

    print(path_src)
    for pattern_name, pattern_id in pattern_ids:
        safe_securify = {
            int(t[2][1:])
            for t in compliant_securify.get(pattern_id, [])
        }
        unsafe_securify = {
            int(t[2][1:])
            for t in violation_securify.get(pattern_id, [])
        }
        warn_securify = {
            int(t[2][1:])
            for t in warnings_securify.get(pattern_id, [])
        }
        conf_securify = {
            int(t[2][1:])
            for t in conflict_securify.get(pattern_id, [])
        }

        (unsafe_securify, safe_securify, warn_securify,
         conf_securify) = get_lines(securify_output, pattern_name)

        elements = [
            safe_securify, warn_securify, unsafe_securify, conf_securify,
            safe_securify, warn_securify, unsafe_securify, conf_securify
        ]

        lines = sorted({item for e in elements for item in e})

        print(pattern_name)
        print("\t".join(["Securify", "", "", "", "Securify"]))
        print("\t".join(["SF", "WRN", "USF", "CNF"] * 2 + ["DIFF"]))

        for line in lines:
            t = [str(line) if line in s else "" for s in elements]
            d = any([t[i] != t[i + 4] for i in range(4)])
            print("\t".join(t + ["X" if d else ""]))

        print()