Source code for swh.vulns.osv.parser.utils

# Copyright (C) 2026  The Software Heritage developers
# See the AUTHORS file at the top-level directory of this distribution
# License: GNU General Public License version 3, or any later version
# See top-level LICENSE file for more information

from datetime import datetime, timezone
import re
from typing import Optional

from swh.model.model import Snapshot, SnapshotTargetType
from swh.model.swhids import CoreSWHID, ObjectType


[docs] def utc_now(): return datetime.now(tz=timezone.utc)
[docs] def find_release_from_version( version: str, snapshot: Snapshot, branch_prefix=b"refs/tags/" ) -> Optional[CoreSWHID]: """Try to find branch in a snapshot corresponding to a specific software origin version using a set of heuristics. Args: version: Version number for a release of a software origin snapshot: Snapshot of a software origin branch_prefix: Only consider branches whose name starts with that prefix Returns: the SWHID of a release or revision related to the version number if found, :const:`None` otherwise """ version = version.lower() branch_names = [ branch_prefix + branch_suffix.encode() for branch_suffix in ( version, version + ".0", "v" + version + ".0", "v" + version, version.replace(".", "_"), "v" + version.replace(".", "_"), version.replace(".", "-"), "v" + version.replace(".", "-"), version.replace("-", "."), "v" + version.replace("-", "."), "v" + version.replace("_", "-"), version.replace("-", "_").replace(".", "_"), "v_" + version.replace("-", "_").replace(".", "_"), ) ] if version.endswith(".0"): branch_names += [ branch_prefix + version.encode()[:-2], branch_prefix + b"v" + version.encode()[:-2], ] if version.endswith("."): branch_names += [ branch_prefix + version.encode()[:-1], branch_prefix + b"v" + version.encode()[:-1], ] if version.endswith("-na"): branch_names += [ branch_prefix + version[:-3].encode(), branch_prefix + b"v" + version[:-3].encode(), ] if m := re.match( r".*(?P<suffix>-(a|b|c|d|p|pre|sp|rc|alpha|beta|update|milestone))[0-9]*$", version, ): replacements = { b"-a": [b"a", b"-alpha"], b"-b": [b"b", b"-beta"], b"-c": [b"c"], b"-d": [b"d"], b"-p": [b"p", b"_", b"-patch"], b"-pre": [b"pre"], b"-sp": [b"-preview", b".0-preview"], b"-rc": [b"rc"], b"-alpha": [b"-a", b"alpha"], b"-beta": [b"-b", b"beta"], b"-update": [b"+"], b"-milestone": [b"m", b"-m", b"_m"], } old_suffix = m.group("suffix").encode() branch_names.extend( branch_name.replace(old_suffix, new_suffix) for branch_name in list(branch_names) for new_suffix in replacements[old_suffix] ) branch_names += [branch_name + b"-release" for branch_name in branch_names] branch_names += [branch_name + b"_release" for branch_name in branch_names] filtered_branches = { kb.lower(): vb for kb, vb in snapshot.branches.items() if kb.lower().endswith( tuple( branch_name.replace(branch_prefix, b"") for branch_name in branch_names ) ) or version.encode() in kb.lower() } found_branch_name = None for branch_name in branch_names: if branch_name.lower() in filtered_branches: found_branch_name = branch_name.lower() break else: if len(filtered_branches): found_branch_name = next(iter(sorted(filtered_branches))) return ( CoreSWHID( object_type=( ObjectType.RELEASE if filtered_branches[found_branch_name].target_type == SnapshotTargetType.RELEASE else ObjectType.REVISION ), object_id=filtered_branches[found_branch_name].target, ) if found_branch_name else None )