diff --git a/src/macaron/malware_analyzer/pypi_heuristics/metadata/anomalous_version.py b/src/macaron/malware_analyzer/pypi_heuristics/metadata/anomalous_version.py index 2f39d6f70..f02c4f595 100644 --- a/src/macaron/malware_analyzer/pypi_heuristics/metadata/anomalous_version.py +++ b/src/macaron/malware_analyzer/pypi_heuristics/metadata/anomalous_version.py @@ -69,7 +69,13 @@ def __init__(self) -> None: self.major_threshold, self.epoch_threshold, self.day_publish_error = self._load_defaults() def _load_defaults(self) -> tuple[int, int, int]: - """Load default settings from defaults.ini.""" + """Load default settings from defaults.ini. + + Returns + ------- + tuple[int, int, int]: + The Major threshold, Epoch threshold, and Day published error. + """ section_name = "heuristic.pypi" if defaults.has_section(section_name): section = defaults[section_name] @@ -78,7 +84,6 @@ def _load_defaults(self) -> tuple[int, int, int]: section.getint("epoch_threshold"), section.getint("day_publish_error"), ) - # Major threshold, Epoch threshold, Day pushlish error return 20, 3, 4 def analyze(self, pypi_package_json: PyPIPackageJsonAsset) -> tuple[HeuristicResult, dict[str, JsonType]]: @@ -172,7 +177,7 @@ def analyze(self, pypi_package_json: PyPIPackageJsonAsset) -> tuple[HeuristicRes elif ( ((version.major in months and version.minor in days) or (version.major in days and version.minor in months)) and version.micro in years - ) or self.__integer_date(version.major, years, months, days): + ) or self._integer_date(version.major, years, months, days): # must include day and year for this to be calendar calendar = True @@ -203,7 +208,36 @@ def analyze(self, pypi_package_json: PyPIPackageJsonAsset) -> tuple[HeuristicRes return HeuristicResult.PASS, detail_info - def __integer_date(self, value: int, years: list[int], months: list[int], days: list[int]) -> bool: + def _integer_date(self, value: int, years: list[int], months: list[int], days: list[int]) -> bool: + """Check whether the provided integer represents a date. + + Valid representations are: + - YYYYMMDD + - YYYYDDMM + - YYDDMM + - YYMMDD + - MMDDYYYY + - DDMMYYYY + - DDMMYY + - MMDDYY + + Parameters + ---------- + value: int + The integer to check. + years: list[int] + A list of integers representing valid years for components of value to represent. + months: list[int] + A list of integers representing valid months for components of value to represent. + days: list[int] + A list of integers representing valid days for components of value to represent. + + Returns + ------- + bool: + True if the integer may represent a date present in the list of valid years, months and days. + False otherwise. + """ for date_format in self.DIGIT_DATE_FORMATS: if (date := parse_datetime(str(value), date_format)) is None: continue