From 8879de8284d8d8adb3d3a920c744e7d3435d6a0d Mon Sep 17 00:00:00 2001 From: Sebastian Strollo Date: Tue, 30 Apr 2024 15:14:08 +0200 Subject: [PATCH] Support git_subdir dependency (and referring to a branch) (#15) * Support git_subdir dependency (and referring to a branch) * Add support for fetching license file from Github --- rebar.config | 5 +- src/rebar3_sbom_prv.erl | 121 +++++++++++++++++++++++++++------------ src/rebar3_sbom_purl.erl | 2 +- 3 files changed, 88 insertions(+), 40 deletions(-) diff --git a/rebar.config b/rebar.config index f618f3e..65360f7 100644 --- a/rebar.config +++ b/rebar.config @@ -1,2 +1,5 @@ {erl_opts, [debug_info]}. -{deps, []}. \ No newline at end of file +{deps, + [ + { jsone, "1.8.1" } + ]}. diff --git a/src/rebar3_sbom_prv.erl b/src/rebar3_sbom_prv.erl index 8babb4b..44519ec 100644 --- a/src/rebar3_sbom_prv.erl +++ b/src/rebar3_sbom_prv.erl @@ -52,58 +52,52 @@ dep_info(Dep) -> Name = rebar_app_info:name(Dep), Version = rebar_app_info:original_vsn(Dep), Source = rebar_app_info:source(Dep), - Dir = rebar_app_info:dir(Dep), Details = rebar_app_info:app_details(Dep), Deps = rebar_app_info:deps(Dep), - dep_info(Name, Version, Source, Dir, Details, Deps). + Common = + [ + {author, proplists:get_value(maintainers, Details)}, + {description, proplists:get_value(description, Details)}, + {licenses, proplists:get_value(licenses, Details)}, + {dependencies, Deps} + ], + dep_info(Name, Version, Source, Common). -dep_info(_Name, _Version, {pkg, Name, Version, Sha256}, _Dir, Details, Deps) -> +dep_info(_Name, _Version, {pkg, Name, Version, Sha256}, Common) -> [ {name, Name}, {version, Version}, - {author, proplists:get_value(maintainers, Details)}, - {description, proplists:get_value(description, Details)}, - {licenses, proplists:get_value(licenses, Details)}, {purl, rebar3_sbom_purl:hex(Name, Version)}, - {sha256, string:lowercase(Sha256)}, - {dependencies, Deps} - ]; + {sha256, string:lowercase(Sha256)} + | Common ]; -dep_info(_Name, _Version, {pkg, Name, Version, _InnerChecksum, OuterChecksum, _RepoConfig}, _Dir, Details, Deps) -> +dep_info(_Name, _Version, {pkg, Name, Version, _InnerChecksum, OuterChecksum, _RepoConfig}, Common) -> [ {name, Name}, {version, Version}, - {author, proplists:get_value(maintainers, Details)}, - {description, proplists:get_value(description, Details)}, - {licenses, proplists:get_value(licenses, Details)}, {purl, rebar3_sbom_purl:hex(Name, Version)}, - {sha256, string:lowercase(OuterChecksum)}, - {dependencies, Deps} - ]; + {sha256, string:lowercase(OuterChecksum)} + | Common ]; -dep_info(Name, _Version, {git, Git, {tag, Tag}}, _Dir, Details, Deps) -> +dep_info(Name, DepVersion, {git, Git, GitRef}, Common) -> + {Version, Purl} = + case GitRef of + {tag, Tag} -> + {Tag, rebar3_sbom_purl:git(Name, Git, Tag)}; + {branch, Branch} -> + {DepVersion, rebar3_sbom_purl:git(Name, Git, Branch)}; + {ref, Ref} -> + {DepVersion, rebar3_sbom_purl:git(Name, Git, Ref)} + end, [ - {name, Name}, - {version, Tag}, - {author, proplists:get_value(maintainers, Details)}, - {description, proplists:get_value(description, Details)}, - {licenses, proplists:get_value(licenses, Details)}, - {purl, rebar3_sbom_purl:git(Name, Git, Tag)}, - {dependencies, Deps} - ]; - -dep_info(Name, Version, {git, Git, {ref, Ref}}, _Dir, Details, Deps) -> - [ - {name, Name}, - {version, Version}, - {author, proplists:get_value(maintainers, Details)}, - {description, proplists:get_value(description, Details)}, - {licenses, proplists:get_value(licenses, Details)}, - {purl, rebar3_sbom_purl:git(Name, Git, Ref)}, - {dependencies, Deps} - ]; - -dep_info(_Name, _Version, _Source, _Dir, _Details, _Deps) -> + {name, Name}, + {version, Version}, + {purl, Purl} + | maybe_update_licenses(Purl, Common) ]; +dep_info(Name, Version, {git_subdir, Git, Ref, _Dir}, Common) -> + dep_info(Name, Version, {git, Git, Ref}, Common); + +dep_info(_Name, _Version, _Source, _Common) -> undefined. write_file(Filename, Xml, true) -> @@ -123,3 +117,54 @@ write_file(Filename, Xml, false) -> Error -> Error end. + +maybe_update_licenses(Purl, Common) -> + case proplists:get_value(licenses, Common) of + [_|_] -> + %% Non-empty list, ok + Common; + _ -> + %% [] or 'undefined' + case Purl of + <<"pkg:github/", GithubPurlString/binary>> -> + case get_github_license(GithubPurlString) of + {ok, SPDX_Id} -> + lists:keyreplace(licenses, 1, Common, + {licenses, [SPDX_Id]}); + _ -> + Common + end; + _ -> + Common + end + end. + +get_github_license(String) -> + case re:split(String, <<"[/@]">>) of + [Org, Repo, _Ref] -> + get_github_license(Org, Repo); + _ -> + {error, string} + end. + +get_github_license(Org, Repo) -> + URI = + #{ scheme => <<"https">>, + path => filename:join([<<"/repos">>, Org, Repo, <<"license">>]), + host => <<"api.github.com">> + }, + URIStr = uri_string:recompose(URI), + Headers = #{<<"user-agent">> => <<"rebar3">>}, + case + rebar_httpc_adapter:request(get, URIStr, Headers, undefined, #{}) + of + {ok, {200, _ReplyHeaders, Body}} -> + case jsone:decode(Body) of + #{<<"license">> := #{<<"spdx_id">> := SPDX_Id}} -> + {ok, SPDX_Id}; + _ -> + {error, body} + end; + _ -> + {error, request} + end. diff --git a/src/rebar3_sbom_purl.erl b/src/rebar3_sbom_purl.erl index 4fabb79..7fee33e 100644 --- a/src/rebar3_sbom_purl.erl +++ b/src/rebar3_sbom_purl.erl @@ -45,7 +45,7 @@ bitbucket(Repo, Ref) -> purl(PathSegments, Version) -> Path = lists:join("/", [escape(Segment) || Segment <- PathSegments]), - io_lib:format("pkg:~s@~s", [Path, escape(Version)]). + unicode:characters_to_binary(io_lib:format("pkg:~s@~s", [Path, escape(Version)])). -if(?OTP_RELEASE >= 25).