diff --git a/prospector/client/cli/prospector_client.py b/prospector/client/cli/prospector_client.py index 9933ec814..9bd7acebf 100644 --- a/prospector/client/cli/prospector_client.py +++ b/prospector/client/cli/prospector_client.py @@ -35,6 +35,23 @@ core_statistics = execution_statistics.sub_collection("core") +def compute_tag_for_version(tag, version): + results = get_tag_for_version(tag, version) + if results.has_matches(): + return results.get_matches() + else: + suggestions = results.get_suggestions() + return interactively_ask_user( + "Could not map supplied version to tag. Please provide mapping by using the '--tag-interval' flag or type it here. Suggestions:" + + str(suggestions) + ) + + +def interactively_ask_user(message): + _logger.warning(message) + return [str(input())] + + # @profile @measure_execution_time(execution_statistics, name="core") def prospector( # noqa: C901 @@ -118,8 +135,10 @@ def prospector( # noqa: C901 prev_tag, following_tag = tag_interval.split(":") elif version_interval != "": vuln_version, fixed_version = version_interval.split(":") - prev_tag = get_tag_for_version(tags, vuln_version)[0] - following_tag = get_tag_for_version(tags, fixed_version)[0] + _logger.info("Determining vulnerable version") + prev_tag = compute_tag_for_version(tags, vuln_version)[0] + _logger.info("Determining fixed version") + following_tag = compute_tag_for_version(tags, fixed_version)[0] since = None until = None diff --git a/prospector/git/test_git.py b/prospector/git/test_git.py index df7f8a1b7..93ae449d8 100644 --- a/prospector/git/test_git.py +++ b/prospector/git/test_git.py @@ -43,7 +43,7 @@ def test_get_tag_for_version(): repo = Git("https://github.com/apache/struts") repo.clone() tags = repo.get_tags() - assert get_tag_for_version(tags, "2.3.9") == ["STRUTS_2_3_9"] + assert get_tag_for_version(tags, "2.3.9").get_suggestions() == ["STRUTS_2_3_9"] # def test_legacy_mapping_version_to_tag_1(): diff --git a/prospector/git/test_version_to_tag.py b/prospector/git/test_version_to_tag.py index 0bce1844b..809546add 100644 --- a/prospector/git/test_version_to_tag.py +++ b/prospector/git/test_version_to_tag.py @@ -1,7 +1,10 @@ import pytest from .test_fixtures import tags -from .version_to_tag import get_tag_for_version, recursively_split_version_string +from .version_to_tag import ( + recursively_split_version_string, + get_tag_for_version, +) # flake8: noqa @@ -35,4 +38,6 @@ def test_recursively_split_version_string_errors(input_version, error): ) def test_get_tag_for_version(version, tag, tags): # returns a list of tags that could be corresponding to the version - assert tag in get_tag_for_version(tags, version) + res = get_tag_for_version(tags, version) + print(res) + assert tag in res.get_matches() diff --git a/prospector/git/version_to_tag.py b/prospector/git/version_to_tag.py index f59d4707c..1612f5316 100644 --- a/prospector/git/version_to_tag.py +++ b/prospector/git/version_to_tag.py @@ -6,12 +6,32 @@ # flake8: noqa import difflib +import logging +import log.util + # pylint: disable=singleton-comparison,unidiomatic-typecheck, dangerous-default-value import re from .git import Commit, Git +_logger = log.util.init_local_logger() + + +class ResultsWithSuggestions: + def __init__(self, matches: list = [], suggestions: list = []): + self.matches = matches # exact or high-confidence results + self.suggestions = suggestions # potantially good suggestions + + def has_matches(self) -> bool: + return len(self.matches) != 0 + + def get_matches(self) -> list: + return self.matches + + def get_suggestions(self) -> list: + return self.suggestions + def recursively_split_version_string(input_version: str, output_version: list = []): """ @@ -51,14 +71,14 @@ def recursively_split_version_string(input_version: str, output_version: list = ) -def get_tag_for_version(tags, version): +def get_tag_for_version(tags, version) -> ResultsWithSuggestions: """ Map a version onto an existing tag Input: tags (list): a list of tags to map version onto version (str): the version Returns: - list: list with tags that could be the version + ResultsWithSuggestions: class with tags that could be the version (both exact matches and suggestions) @TODO: only return the most relevant tag i.e. for key 8 version 4.1 returns ['version-3.4.1', 'version-4.1', 'version-4.4.1'] """ if isinstance(tags, tuple): @@ -104,9 +124,9 @@ def get_tag_for_version(tags, version): elif version in stripped_tags and stripped_tags.count(version) == 1: tag = tags[stripped_tags.index(version)] elif version in stripped_tags and stripped_tags.count(version) > 1: - return [ - tags[index] for index, tag in enumerate(stripped_tags) if tag == version - ] + return ResultsWithSuggestions( + [tags[index] for index, tag in enumerate(stripped_tags) if tag == version] + ) elif ( stripped_version in stripped_tags and stripped_tags.count(stripped_version) == 1 ): @@ -114,12 +134,13 @@ def get_tag_for_version(tags, version): elif ( stripped_version in stripped_tags and stripped_tags.count(stripped_version) > 1 ): - return [ - tags[index] - for index, tag in enumerate(stripped_tags) - if tag == stripped_version - ] - + return ResultsWithSuggestions( + [ + tags[index] + for index, tag in enumerate(stripped_tags) + if tag == stripped_version + ] + ) else: version = re.sub("[^0-9]", "", version) best_match = ("", 0.0) @@ -128,8 +149,9 @@ def get_tag_for_version(tags, version): match_score = difflib.SequenceMatcher(None, t_strip, version).ratio() if match_score > best_match[1]: best_match = (tag, match_score) - tag = best_match[0] - return [tag] + tag = [best_match[0]] + return ResultsWithSuggestions(suggestions=tag) + return ResultsWithSuggestions(matches=[tag]) # def get_timestamp_for_tag(tag, git_repo):