Beispiel #1
0
def is_running_latest() -> bool:
    try:
        latest, force = fetch_latest_version()
        curent = get_version()
        if latest:
            latest_version = Version(latest, partial=True)
            current_version = Version(curent, partial=True)
            if current_version < latest_version:
                if force:
                    print_msg(UPGRADE_WARNING_OUTPUT)
                    print_error_exit(
                        "Something is wrong with your CLI version. You must upgrade to continue"
                    )
                return False
            else:
                return True
        else:
            # fail safe
            logger.info("Unable to get latest cli version from server")
            return True
    except Exception as e:
        # fail safe
        logger.info(
            f"Unexpected error comparing latest and current version: {e}")
        return True
Beispiel #2
0
def do_login(org: Optional[str] = None,
             login_token: Optional[str] = None) -> Optional[str]:
    # ensure org
    if org is None:
        org = get_default_org()
        if org is None:
            org = PLATFORM_ANALYZER_PREFIX
    if not login_token:
        if click.confirm(
                "Opening web browser to get login token. Do you want to continue?",
                default=True,
        ):
            open_browser_login(org)
        else:
            url = get_authentication_url(org)
            print_msg(f"Visit {url} and enter the token below")
        # prompt for token
        for attempt in range(MAX_RETRIES):
            # validate token
            token = check_valid_token_with_logging(
                org, click.prompt("Please enter the API token",
                                  hide_input=True))
            if token:
                return token
        print_error_exit(
            "Max attempts exceeded. Please contact R2C support for help")
    else:
        return check_valid_token_with_logging(org, login_token)

    return None
Beispiel #3
0
def open_browser_login(org: Optional[str]) -> None:
    url = get_authentication_url(org)
    print_msg(f"trying to open {url} in your browser...")
    try:
        webbrowser.open(url, new=0, autoraise=True)
    except Exception:
        print_msg(
            f"Unable to open a web browser. Please visit {url} and paste the token in here"
        )
Beispiel #4
0
def cli(ctx, debug, verbose, no_traverse_manifest):
    ctx.ensure_object(dict)
    if not is_running_latest():
        print_msg(UPGRADE_WARNING_OUTPUT)
    if not is_running_supported_python3():
        print_error("Please upgrade to python3.6 to run r2c-cli.")
    set_debug_flag(ctx, debug)
    set_verbose_flag(ctx, verbose)
    ctx.obj["NO_TRAVERSE_MANIFEST"] = no_traverse_manifest
Beispiel #5
0
 def login_retry_wrapper(*args, **kwargs):
     r = fn(*args, **kwargs)
     if r.status_code in BAD_AUTH_CODES:
         print_warning(
             "Something is wrong with your credentials. Let's login and try again..."
         )
         new_token = do_login()
         print_msg("Back to what we were doing...")
         r = fn(*args, **kwargs, token=new_token)
     return r
Beispiel #6
0
def build(ctx, analyzer_directory, env_args_string):
    """Builds an analyzer without running it.
    """

    manifest, analyzer_directory = find_and_open_analyzer_manifest(
        analyzer_directory, ctx)

    print_msg("🔨 Building docker container")

    abort_on_build_failure(
        build_docker(
            manifest.analyzer_name,
            manifest.version,
            os.path.relpath(analyzer_directory, os.getcwd()),
            env_args_dict=parse_remaining(env_args_string),
        ))
Beispiel #7
0
def upload_inputset(ctx, input_set_file, verbose, debug):
    """
    Uploads INPUT_SET_FILE to the r2c analysis platform as a custom input set.
    See http://docs.r2c.dev for how to properly format INPUT_SET_FILE.
    """
    if verbose is True:  # allow passing --verbose to run as well as globally
        set_verbose_flag(ctx, True)
    if debug is True:
        set_debug_flag(ctx, True)
    print_msg(f"Uploading input set defined in `{input_set_file}`...")
    with open(input_set_file) as f:
        try:
            input_set = json.load(f)
        except Exception:
            print_error_exit(
                "Could not parse input set. Make sure it's a proper json file!"
            )
    r = auth_post(f"{get_base_url()}/api/v1/inputs/", input_set)
    handle_request_with_error_message(r)
    print_success("Uploaded new input set successfully")
Beispiel #8
0
def run(
    ctx,
    analyzer_directory,
    analyzer_input,
    output_path,
    quiet,
    analyzer_quiet,
    no_login,
    debug,
    interactive,
    interactive_name,
    reset_cache,
    verbose,
    parameters,
    env_args_string,
):
    """
    Run the analyzer in the current directory over a code directory.

    You may have to log in if your analyzer depends on privately
    published analyzers.
    """

    if verbose is True:  # allow passing --verbose to run as well as globally
        set_verbose_flag(ctx, True)
    if debug is True:
        set_debug_flag(ctx, True)
    print_msg(f"🏃 Starting to run analyzer...")

    interactive_index = -1 if interactive else None
    env_args_dict = parse_remaining(env_args_string)

    parameter_obj = load_params(parameters)

    manifest, analyzer_directory = find_and_open_analyzer_manifest(
        analyzer_directory, ctx)

    registry_data = get_registry_data()

    dependencies = manifest.dependencies
    print_msg("Resolving dependencies")
    logger.debug(f"Parsing and resolving dependencies: {dependencies}")
    if dependencies:
        for analyzer_dep in dependencies:
            dep_name = analyzer_dep.name
            dep_semver_version = analyzer_dep.wildcard_version
            dep_version = registry_data._resolve(
                AnalyzerName(analyzer_dep.name), dep_semver_version)
            if not dep_version:
                if not analyzer_dep.path:
                    print_error_exit(
                        f"Error resolving dependency {dep_name} at version {dep_semver_version}. Check that you're using the right version of this dependency and try again."
                    )
            logger.debug(
                f"Resolved dependency {dep_name}:{dep_semver_version}")

        if not no_login:
            # we need at least one dep and its version to get credentials when the user isn't logged in
            dep_name = dependencies[0].name
            dep_semver_version = dependencies[0].wildcard_version
            dep_version = registry_data._resolve(AnalyzerName(dep_name),
                                                 dep_semver_version)

            artifact_link = (
                f"{get_base_url()}/api/v1/artifacts/{dep_name}/{dep_version}")
            logger.debug(f"Getting credential from {artifact_link}")

            # TODO (ulzii) use proper auth credential once its done
            creds = get_docker_creds(artifact_link)
            if creds is None:
                print_error_exit(
                    "Error getting dependency credentials. Please contact us with the following information: failed to get Docker credentials."
                )
            # docker login
            successful_login = docker_login(creds)
            if not successful_login:
                print_error_exit(
                    "Error validating dependency credentials. Please contact us with the following information: failed to log in to Docker."
                )
    else:
        print_warning(
            "No dependencies found; are dependencies intentionally omitted in analyzer.json? Most analyzers are expected to have 1 or more dependencies (e.g. for taking source code as input)."
        )
    print_msg("🔨 Building docker container")

    abort_on_build_failure(
        build_docker(
            manifest.analyzer_name,
            manifest.version,
            os.path.relpath(analyzer_directory, os.getcwd()),
            env_args_dict=env_args_dict,
            no_cache=reset_cache,
        ))
    try:
        if interactive_index:
            print_msg(
                f"🔎 Inspecting containers interactively by `docker exec` into last analyzer in execution."
            )
        elif interactive_name:
            print_msg(
                f"🔎 Inspecting containers interactively by `docker exec` into analyzer with name containing `{interactive_name}`."
            )
        else:
            print_msg(f"🔎 Running analysis on `{analyzer_input}`")

        logger.info(f"Reset cache: {reset_cache}")
        try:
            run_analyzer_on_local_code(
                registry_data=registry_data,
                manifest=manifest,
                workdir=None,
                analyzer_dir=analyzer_directory,
                code_dir=analyzer_input,
                output_path=output_path,
                show_output_on_stdout=not quiet,
                pass_analyzer_output=not analyzer_quiet,
                no_preserve_workdir=True,
                parameters=parameter_obj,
                env_args_dict=env_args_dict,
                interactive_index=interactive_index,
                interactive_name=interactive_name,
                reset_cache=reset_cache,
            )
        except AnalyzerOutputNotFound as fne:
            print_error_exit(str(fne), err=False)
        except AnalyzerNonZeroExitError as ae:
            print_exception_exit("Analyzer non-zero exit", ae, err=False)
        if output_path:
            path_msg = f"Analysis results in `{output_path}`."
        else:
            path_msg = f"Analysis results printed to `stdout`. unless suppressed explicitly with `-q`"
        print_success(f"Finished analyzing `{analyzer_input}`. {path_msg}")

    except SymlinkNeedsElevationError as sym:
        print_error_exit(
            f"Error setting up local analysis. {sym}. Try again as an admin.")
Beispiel #9
0
def test(ctx, analyzer_directory, which, cache, env_args_string):
    """
    Locally run tests for the current analyzer.

    You can add integration test files to the `examples/` directory.
    For more information, refer to the integration test section of the README.

    For unittests, you can define how to run your unit tests in `src/unittest.sh`.

    You may have to login if your analyzer depends on privately
    published analyzers.
    """

    verbose = ctx.obj["VERBOSE"]
    env_args_dict = parse_remaining(env_args_string)
    print_msg(
        f"Running integration tests for analyzer {'with debug mode' if ctx.obj['DEBUG'] else ''}"
    )

    manifest, analyzer_directory = find_and_open_analyzer_manifest(
        analyzer_directory, ctx
    )
    check_docker_is_running()
    print_msg("🔨 Building docker container")
    abort_on_build_failure(
        build_docker(
            manifest.analyzer_name,
            manifest.version,
            os.path.relpath(analyzer_directory, os.getcwd()),
            env_args_dict=env_args_dict,
        )
    )
    if which == "unit" or which == "all":
        image_id = VersionedAnalyzer(manifest.analyzer_name, manifest.version).image_id
        status = run_docker_unittest(
            analyzer_directory=analyzer_directory,
            analyzer_name=manifest.analyzer_name,
            docker_image=image_id,
            verbose=verbose,
            env_args_dict=env_args_dict,
        )
        if status == 0:
            print_success(f"Unit tests passed")
        else:
            print_error(f"Unit tests failed with status {status}")
    if which == "integration" or which == "all":
        try:
            passed_all = integration_test(
                manifest=manifest,
                analyzer_directory=analyzer_directory,
                workdir=None,
                verbose=verbose,
                env_args_dict=env_args_dict,
                registry_data=get_registry_data(),
                use_cache=cache,
            )
            if passed_all:
                print_success(f"All integration tests passed")
            else:
                print_error_exit(f"Some integration tests failed", status_code=-1)

        except SymlinkNeedsElevationError as sym:
            print_error(
                f"Error setting up integration tests. {sym}. Try again as an admin"
            )
Beispiel #10
0
def _print_version(ctx, param, value):
    """Print the current r2c-cli version based on setuptools runtime"""
    if not value or ctx.resilient_parsing:
        return
    print_msg(f"r2c-cli/{get_version()}")
    ctx.exit()
Beispiel #11
0
def push(ctx, analyzer_directory, force, squash, env_args_string):
    """
    Push the analyzer in the current directory to the R2C platform.

    You must log in to push analyzers.

    This command will validate your analyzer and privately publish your analyzer
    to your org with the name specified in analyzer.json.

    Your analyzer name must follow {org}/{name}.
    """
    env_args_dict = parse_remaining(env_args_string)

    manifest, analyzer_directory = find_and_open_analyzer_manifest(
        analyzer_directory, ctx
    )
    readme = find_and_open_analyzer_readme(analyzer_directory, ctx)
    analyzer_org = get_org_from_analyzer_name(manifest.analyzer_name)

    overwriting_message = (
        " and forcing overwrite if the analyzer version exists and is pending upload."
    )
    # TODO(ulzii): let's decide which source of truth we're using for analyzer_name above and/or check consistency.
    # can't have both dir name and what's in analyzer.json
    print_msg(
        f"📌 Pushing analyzer in {analyzer_directory}{overwriting_message if force else ''}..."
    )

    default_org = get_default_org()
    if default_org is None:
        print_error_exit(
            f"You are not logged in. Please run `r2c login` to be able to push your analyzer"
        )

    if default_org != analyzer_org:
        if analyzer_org != PLATFORM_ANALYZER_PREFIX:
            print_error_exit(
                f"You're logged in to the common r2c platform. The org specified as the prefix of your analyzer name in `analyzer.json` must be `{PLATFORM_ANALYZER_PREFIX}`. "
                + f"Replace `{analyzer_org}` with `{PLATFORM_ANALYZER_PREFIX}` and try again."
                + "Please ask for help from r2c support"
            )
    try:
        # upload analyzer.json
        artifact_link = _upload_analyzer_manifest(manifest, force, readme)
    except Exception as e:
        print_exception_exit("There was an error uploading your analyzer", e)
    if artifact_link is None:
        print_error_exit(
            "There was an error uploading your analyzer. Please ask for help from R2C support"
        )
    get_logger().info(f"using artifact link: {artifact_link}")
    # get docker login creds
    creds = get_docker_creds(artifact_link)
    if creds is None:
        print_error_exit(
            "There was an error getting Docker credentials. Please ask for help from R2C support"
        )
    else:
        print_success_step("Successfully fetched credentials.")

    # docker login
    successful_login = docker_login(creds)
    if not successful_login:
        print_error_exit(
            "There was an error logging into Docker. Please ask for help from R2C support"
        )
    else:
        print_success_step("Successfully logged in to docker.")

    print_msg("🔨 Building docker container")
    # docker build and tag
    abort_on_build_failure(
        build_docker(
            manifest.analyzer_name,
            manifest.version,
            os.path.relpath(analyzer_directory, os.getcwd()),
            env_args_dict=env_args_dict,
            squash=squash,
        )
    )
    # docker push
    image_id = VersionedAnalyzer(manifest.analyzer_name, manifest.version).image_id
    print_msg(f"Pushing docker container to `{analyzer_org}`")
    successful_push = _docker_push(image_id)
    if not successful_push:
        print_error_exit(
            "There was an error pushing the Docker image. Please ask for help from R2C support"
        )
    else:
        print_success_step("Successfully pushed to R2C.")
    # mark uploaded with API
    # TODO figure out how to determine org from analyzer.json
    try:
        uploaded_url = f"{get_base_url()}/api/v1/analyzers/{manifest.analyzer_name}/{manifest.version}/uploaded"
        r = auth_put(uploaded_url)
        data = handle_request_with_error_message(r)
        if data.get("status") == "uploaded":
            web_url = data["links"]["web_url"]
            # display status to user and give link to view in web UI
            print_success(
                f"Upload finished successfully for analyzer! Visit: {web_url}"
            )
        else:
            print_error_exit("Error confirming analyzer was successfully uploaded.")
    except Exception as e:
        print_exception_exit("Error confirming analyzer was successfully uploaded", e)