Skip to main content

src/rebar_sbom_purl.erl

%% SPDX-License-Identifier: BSD-3-Clause
%% SPDX-FileCopyrightText: 2020 Andrew Mayorov
%% SPDX-FileCopyrightText: 2022 Bram Verburg
%% SPDX-FileCopyrightText: 2024 Sebastian Strollo
%% SPDX-FileCopyrightText: 2025 Stritzinger GmbH

-module(rebar_sbom_purl).

% https://github.com/package-url/purl-spec

-export([hex/2, git/3, github/2, bitbucket/2, local_otp_app/2, local/2]).

hex(Name, Version) ->
    purl(["hex", string:lowercase(Name)], Version).

git(_Name, "git@github.com:" ++ Github, Ref) ->
    Repo = string:replace(Github, ".git", "", trailing),
    github(Repo, Ref);
git(_Name, "https://github.com/" ++ Github, Ref) ->
    Repo = string:replace(Github, ".git", "", trailing),
    github(Repo, Ref);
git(_Name, "git://github.com/" ++ Github, Ref) ->
    Repo = string:replace(Github, ".git", "", trailing),
    github(Repo, Ref);
git(_Name, "git@bitbucket.org:" ++ Github, Ref) ->
    Repo = string:replace(Github, ".git", "", trailing),
    bitbucket(Repo, Ref);
git(_Name, "https://bitbucket.org/" ++ Github, Ref) ->
    Repo = string:replace(Github, ".git", "", trailing),
    bitbucket(Repo, Ref);
git(_Name, "git://bitbucket.org/" ++ Github, Ref) ->
    Repo = string:replace(Github, ".git", "", trailing),
    bitbucket(Repo, Ref);
%% Git dependence other than GitHub and BitBucket are not currently supported
git(_Name, _Git, _R) ->
    undefined.

github(Repo, Ref) ->
    [Organization, Name | _] = string:split(Repo, "/"),
    purl(["github", string:lowercase(Organization), string:lowercase(Name)], Ref).

bitbucket(Repo, Ref) ->
    [Organization, Name | _] = string:split(Repo, "/"),
    purl(["bitbucket", string:lowercase(Organization), string:lowercase(Name)], Ref).

local_otp_app(Name, Version) ->
    purl(["otp", string:lowercase(Name)], Version).

local(Name, Version) ->
    purl(["generic", string:lowercase(Name)], Version).

purl(PathSegments, Version) ->
    Path = lists:join("/", [escape(Segment) || Segment <- PathSegments]),
    unicode:characters_to_binary(io_lib:format("pkg:~s@~s", [Path, escape(Version)])).

-if(?OTP_RELEASE >= 25).

escape(String) ->
    uri_string:quote(String).

-else.

escape(String) ->
    http_uri:encode(String).

-endif.