Exemplo n.º 1
0
def cli(ctx, **kwargs):
    """Your CLI for interacting with https://mythx.io/
    \f

    :param ctx: Click context holding group-level parameters
    :param debug: Boolean to enable the `logging` debug mode
    :param api_key: User JWT access token from the MythX dashboard
    :param username: The MythX account ETH address/username
    :param password: The account password from the MythX dashboard
    :param fmt: The formatter to use for the subcommand output
    :param ci: Boolean to return exit code 1 on medium/high-sev issues
    :param output: Output file to write the results into
    """

    ctx.obj = dict(kwargs)
    ctx.obj["retval"] = 0
    toolname_mw = ClientToolNameMiddleware(
        name="mythx-cli-{}".format(__version__))
    if kwargs["api_key"] is not None:
        ctx.obj["client"] = Client(access_token=kwargs["api_key"],
                                   middlewares=[toolname_mw])
    elif kwargs["username"] and kwargs["password"]:
        ctx.obj["client"] = Client(eth_address=kwargs["username"],
                                   password=kwargs["password"],
                                   middlewares=[toolname_mw])
    else:
        raise click.UsageError((
            "The trial user has been deprecated. You can still use the MythX CLI for free "
            "by signing up for a free account at https://mythx.io/ and entering your access "
            "credentials."))
    if kwargs["debug"]:
        for name in logging.root.manager.loggerDict:
            logging.getLogger(name).setLevel(logging.DEBUG)
Exemplo n.º 2
0
    def get_mythx_client() -> Client:
        """Generate a MythX client instance.

        This method will look for an API key passed as a parameter, and if none
        is found, look for a key in the environment variable :code:`MYTHX_API_KEY`.
        If a key is detected, a PythX client instance is returned, otherwise a
        :code:`ValidationError` is raised.

        :raises: ValidationError if no valid API key is provided
        :return: A PythX client instance
        """

        if CONFIG.argv["api-key"]:
            auth_args = {"api_key": CONFIG.argv["api-key"]}
        elif environ.get("MYTHX_API_KEY"):
            auth_args = {"api_key": environ.get("MYTHX_API_KEY")}
        else:
            raise ValidationError(
                "You must provide a MythX API key via environment variable or the command line"
            )

        return Client(**auth_args,
                      middlewares=[
                          ClientToolNameMiddleware(
                              name=f"brownie-{__version__}")
                      ])
Exemplo n.º 3
0
def main():
    parser = argparse.ArgumentParser(
        description="Submit analysis to MythX API")
    create_parser(parser)
    args = parser.parse_args()

    config["endpoints"]["production"] = args.api_url

    handler = APIHandler(middlewares=[
        ClientToolNameMiddleware(name="ApiTests"),
        AnalysisCacheMiddleware(no_cache=args.no_cache),
    ])
    logging.info(f"Running MythX API tests without cache: {args.no_cache}")
    c = Client(eth_address=ETH_ADDRESS, password=PASSWORD, handler=handler)
    logging.info(f"Submit analysis to API: {args.api_url}")
    resp = c.analyze(**API_PAYLOAD)
    logging.info(resp.to_dict())

    while not c.analysis_ready(resp.uuid):
        logging.info("Checking analysis status")
        time.sleep(1)

    issues = c.report(resp.uuid)
    detected_issues = issues.to_dict()
    logging.info(json.dumps(detected_issues, indent=2))
    return detected_issues
Exemplo n.º 4
0
def cli(ctx, **kwargs):
    """Your CLI for interacting with https://mythx.io/
    \f

    :param ctx: Click context holding group-level parameters
    :param debug: Boolean to enable the `logging` debug mode
    :param access_token: User JWT access token from the MythX dashboard
    :param eth_address: The MythX account ETH address/username
    :param password: The account password from the MythX dashboard
    :param staging: Boolean to redirect requests to MythX staging
    :param fmt: The formatter to use for the subcommand output
    """

    ctx.obj = dict(kwargs)
    toolname_mw = ClientToolNameMiddleware(
        name="mythx-cli-{}".format(__version__))
    if kwargs["access_token"] is not None:
        ctx.obj["client"] = Client(
            access_token=kwargs["access_token"],
            staging=kwargs["staging"],
            middlewares=[toolname_mw],
        )
    elif kwargs["eth_address"] and kwargs["password"]:
        ctx.obj["client"] = Client(
            eth_address=kwargs["eth_address"],
            password=kwargs["password"],
            staging=kwargs["staging"],
            middlewares=[toolname_mw],
        )
    else:
        # default to trial user client
        ctx.obj["client"] = Client(
            eth_address="0x0000000000000000000000000000000000000000",
            password="******",
            staging=kwargs["staging"],
            middlewares=[toolname_mw],
        )
    if kwargs["debug"]:
        for name in logging.root.manager.loggerDict:
            logging.getLogger(name).setLevel(logging.DEBUG)

    return 0
Exemplo n.º 5
0
def get_client() -> Client:
    eth_address = environ.get("PYTHX_USERNAME") or click.prompt(
        "Please enter your Ethereum address",
        type=click.STRING,
        default="0x0000000000000000000000000000000000000000",
    )
    password = environ.get("PYTHX_PASSWORD") or click.prompt(
        "Please enter your MythX password",
        type=click.STRING,
        hide_input=True,
        default="trial",
    )
    c = Client(eth_address=eth_address, password=password, staging=False)
    c.login()
    return c
Exemplo n.º 6
0
def get_mythx_client():
    # if both eth address and username are None,
    if ARGV["access-token"]:
        authenticated = True
        auth_args = {"access_token": ARGV["access-token"]}
    elif ARGV["eth-address"] and ARGV["password"]:
        authenticated = True
        auth_args = {
            "eth_address": ARGV["eth-address"],
            "password": ARGV["password"]
        }
    elif environ.get("MYTHX_ACCESS_TOKEN"):
        authenticated = True
        auth_args = {"access_token": environ.get("MYTHX_ACCESS_TOKEN")}
    elif environ.get("MYTHX_ETH_ADDRESS") and environ.get("MYTHX_PASSWORD"):
        authenticated = True
        auth_args = {
            "eth_address": environ.get("MYTHX_ETH_ADDRESS"),
            "password": environ.get("MYTHX_PASSWORD"),
        }
    else:
        authenticated = False
        auth_args = {
            "eth_address": "0x0000000000000000000000000000000000000000",
            "password": "******",
        }

    return (
        Client(
            **auth_args,
            middlewares=[
                ClientToolNameMiddleware(name="brownie-{}".format(__version__))
            ],
        ),
        authenticated,
    )
Exemplo n.º 7
0
def analyze(contracts: List[SolidityContract],
            analysis_mode: str = "quick") -> Report:
    """
    Analyze contracts via the MythX API.

    :param contracts: List of solidity contracts to analyze
    :param analysis_mode: The mode to submit the analysis request with. "quick" or "full" (default: "quick")
    :return: Report with analyzed contracts
    """
    assert analysis_mode in ("quick",
                             "full"), "analysis_mode must be 'quick' or 'full'"

    c = Client(
        eth_address=os.environ.get("MYTHX_ETH_ADDRESS", TRIAL_ETH_ADDRESS),
        password=os.environ.get("MYTHX_PASSWORD", TRIAL_PASSWORD),
    )

    if c.eth_address == TRIAL_ETH_ADDRESS:
        print(
            "You are currently running MythX in Trial mode. This mode reports only a partial analysis of your smart contracts, limited to three vulnerabilities. To get a more complete analysis, sign up for a free account at https://mythx.io."
        )

    issues = []  # type: List[Issue]

    # TODO: Analyze multiple contracts asynchronously.
    for contract in contracts:
        source_codes = {}
        source_list = []
        sources = {}  # type: Dict[str, Any]
        main_source = None

        try:
            main_source = contract.input_file
            for solidity_file in contract.solidity_files:
                source_codes[solidity_file.filename] = solidity_file.data
            for filename in contract.solc_json["sources"].keys():
                sources[filename] = {}
                if source_codes[filename]:
                    sources[filename]["source"] = source_codes[filename]
                sources[filename]["ast"] = contract.solc_json["sources"][
                    filename]["ast"]

                source_list.append(filename)

            source_list.sort(
                key=lambda fname: contract.solc_json["sources"][fname]["id"])
        except AttributeError:
            # No solidity file
            pass

        assert contract.creation_code, "Creation bytecode must exist."
        try:
            resp = c.analyze(
                contract_name=contract.name,
                analysis_mode=analysis_mode,
                bytecode=contract.creation_code or None,
                deployed_bytecode=contract.code or None,
                sources=sources or None,
                main_source=main_source,
                source_list=source_list or None,
            )
        except MythXAPIError as e:
            log.critical(e)

        while not c.analysis_ready(resp.uuid):
            log.info(c.status(resp.uuid).analysis)
            time.sleep(5)

        for issue in c.report(resp.uuid):
            issue = Issue(
                contract=contract.name,
                function_name=None,
                address=issue.locations[0].source_map.components[0].offset
                if issue.locations else -1,
                swc_id=issue.swc_id[4:] or "None",  # remove 'SWC-' prefix
                title=issue.swc_title,
                bytecode=contract.creation_code,
                severity=issue.severity.capitalize(),
                description_head=issue.description_short,
                description_tail=issue.description_long,
            )
            issue.add_code_info(contract)
            issues.append(issue)

    report = Report(contracts=contracts)
    for issue in issues:
        report.append_issue(issue)

    return report
Exemplo n.º 8
0
def cli(
    ctx,
    debug: bool,
    api_key: str,
    username: str,
    password: str,
    fmt: str,
    ci: bool,
    output: str,
    yes: bool,
    config: str,
    stdout: bool,
) -> None:
    """Your CLI for interacting with https://mythx.io/

    \f

    :param ctx: Click context holding group-level parameters
    :param debug: Boolean to enable the `logging` debug mode
    :param api_key: User JWT api token from the MythX dashboard
    :param username: The MythX account ETH address/username
    :param password: The account password from the MythX dashboard
    :param fmt: The formatter to use for the subcommand output
    :param ci: Boolean to return exit code 1 on medium/high-sev issues
    :param output: Output file to write the results into
    :param config: YAML config file to read default parameters from
    :param stdout: Force printing to stdout and ignore output files
    """

    # set loggers to debug mode
    if debug:
        for name in logging.root.manager.loggerDict:
            logging.getLogger(name).setLevel(logging.DEBUG)

    ctx.obj = {
        "debug": debug,
        "api_key": api_key,
        "username": username,
        "password": password,
        "fmt": fmt,
        "ci": ci,
        "output": output,
        "yes": yes,
        "config": config,
    }

    LOGGER.debug("Initializing configuration context")
    config_file = config or ".mythx.yml"
    if Path(config_file).is_file():
        LOGGER.debug(f"Parsing config at {config_file}")
        with open(config_file) as config_f:
            parsed_config = yaml.safe_load(config_f.read())
    else:
        parsed_config = {"analyze": {}}

    # The analyze context is updated separately in the command
    # implementation
    ctx.obj["analyze"] = parsed_config.get("analyze", {})

    # overwrite context with top-level YAML config keys if necessary
    update_context(ctx.obj, "ci", parsed_config, "ci", False)
    if stdout:
        # if forced stdout, don't set output file
        ctx.obj["output"] = None
    else:
        update_context(ctx.obj, "output", parsed_config, "output", None)
    update_context(ctx.obj, "fmt", parsed_config, "format", "table")
    update_context(ctx.obj, "yes", parsed_config, "confirm", False)

    # set return value - used for CI failures
    ctx.obj["retval"] = 0

    LOGGER.debug(f"Initializing tool name middleware with {__version__}")
    toolname_mw = ClientToolNameMiddleware(
        name="mythx-cli-{}".format(__version__))

    if api_key is not None:
        LOGGER.debug("Initializing client with API key")
        ctx.obj["client"] = Client(api_key=api_key, middlewares=[toolname_mw])
    elif username and password:
        LOGGER.debug("Initializing client with username and password")
        ctx.obj["client"] = Client(username=username,
                                   password=password,
                                   middlewares=[toolname_mw])
    else:
        raise click.UsageError((
            "The trial user has been deprecated. You can still use the MythX CLI for free "
            "by signing up for a free account at https://mythx.io/ and entering your access "
            "credentials."))
Exemplo n.º 9
0
try:
    os.mkdir(OUTPUT_DIR)
except:
    traceback.print_exc()

# Script Set up
try:
    environment = utils.grabEnvFile()
    binaries = os.listdir("./outputs/compiled_bin")

except:
    raise Exception("Init Script Environment")

# Init Mythx Client

c = Client(api_key=environment.get("MYTHX_API_KEY"))
log_string = ""

for byte_file in binaries:
    print("Analyzing: %s" % byte_file)

    byte: str = ""

    try:
        byte = str(
            open(os.path.join(ROOT_BINARIES_DIR, byte_file), "r").read())
        assert len(byte) > 0
    except:
        print("Data Error in: %s ... Skipping" % byte_file)
        continue