diff --git a/apps/rebar/src/rebar.app.src.script b/apps/rebar/src/rebar.app.src.script index 36ae2dd45..afae095c1 100644 --- a/apps/rebar/src/rebar.app.src.script +++ b/apps/rebar/src/rebar.app.src.script @@ -17,6 +17,7 @@ common_test, dialyzer, public_key, + ssh, edoc, snmp, getopt, diff --git a/apps/rebar/src/rebar_hex_auth.erl b/apps/rebar/src/rebar_hex_auth.erl new file mode 100644 index 000000000..0a166e037 --- /dev/null +++ b/apps/rebar/src/rebar_hex_auth.erl @@ -0,0 +1,211 @@ +%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- +%% ex: ts=4 sw=4 et +%% @doc +%% Authentication wrapper for Hex package manager. +%% +%% This module provides rebar3-specific callbacks for r3_hex_cli_auth. +%% See r3_hex_cli_auth for auth resolution order and details. +%% @end +-module(rebar_hex_auth). + +-export([with_api/5, with_repo/4]). + +%% OAuth utilities +-export([client_id/0, global_oauth_key/0, persist_tokens/4]). + + +-include("rebar.hrl"). +-include_lib("providers/include/providers.hrl"). + +-define(GLOBAL_OAUTH_KEY, <<"$oauth">>). +-define(OAUTH_CLIENT_ID, <<"b9721cf5-be2f-4a65-bfa5-141698b4b9cf">>). + +%% @doc Execute an API call with authentication handling. +%% @see r3_hex_cli_auth:with_api/5 +-spec with_api(Permission, Config, State, Opts, Callback) -> Result when + Permission :: r3_hex_cli_auth:permission(), + Config :: rebar_hex_repos:repo(), + State :: rebar_state:t(), + Opts :: r3_hex_cli_auth:opts(), + Callback :: fun((rebar_hex_repos:repo()) -> Result), + Result :: term(). +with_api(Permission, Config, State, Opts, Callback) when Permission =:= read; Permission =:= write -> + Callbacks = make_callbacks(State), + HexConfig = to_hex_config(Config), + r3_hex_cli_auth:with_api(Callbacks, Permission, HexConfig, Callback, Opts). + +%% @doc Execute a repository call with authentication handling. +%% @see r3_hex_cli_auth:with_repo/4 +-spec with_repo(Config, State, Opts, Callback) -> Result when + Config :: rebar_hex_repos:repo(), + State :: rebar_state:t(), + Opts :: r3_hex_cli_auth:opts(), + Callback :: fun((rebar_hex_repos:repo()) -> Result), + Result :: term(). +with_repo(Config, State, Opts, Callback) -> + Callbacks = make_callbacks(State), + HexConfig = to_hex_config(Config), + r3_hex_cli_auth:with_repo(Callbacks, HexConfig, Callback, Opts). + +%% @private +%% Strip rebar3-specific fields from repo config before passing to hex_core. +-spec to_hex_config(rebar_hex_repos:repo()) -> r3_hex_core:config(). +to_hex_config(Config) -> + maps:without([name, parent, mirror_of], Config). + +%%==================================================================== +%% OAuth utilities +%%==================================================================== + +%% @doc Returns the OAuth client ID for Hex. +-spec client_id() -> binary(). +client_id() -> ?OAUTH_CLIENT_ID. + +%% @doc Returns the key used for global OAuth storage in hex.config. +-spec global_oauth_key() -> binary(). +global_oauth_key() -> ?GLOBAL_OAUTH_KEY. + +%% @doc Persist the global OAuth tokens to hex.config. +-spec persist_tokens(AccessToken, RefreshToken, ExpiresAt, State) -> ok when + AccessToken :: binary(), + RefreshToken :: binary() | undefined, + ExpiresAt :: integer(), + State :: rebar_state:t(). +persist_tokens(AccessToken, RefreshToken, ExpiresAt, State) -> + Updates = #{ + ?GLOBAL_OAUTH_KEY => #{ + access_token => AccessToken, + refresh_token => RefreshToken, + expires_at => ExpiresAt + } + }, + rebar_hex_repos:update_auth_config(Updates, State), + ?DEBUG("Updated global OAuth tokens", []), + ok. + +%%==================================================================== +%% Callbacks builder +%%==================================================================== + +%% @private +%% Build the callbacks map required by r3_hex_cli_auth. +-spec make_callbacks(rebar_state:t()) -> r3_hex_cli_auth:callbacks(). +make_callbacks(State) -> + #{ + get_auth_config => fun(RepoName) -> + get_repo_auth_config(RepoName, State) + end, + + get_oauth_tokens => fun() -> + get_global_oauth_tokens(State) + end, + + persist_oauth_tokens => fun(Scope, AccessToken, RefreshToken, ExpiresAt) -> + persist_oauth_tokens(Scope, AccessToken, RefreshToken, ExpiresAt, State) + end, + + prompt_otp => fun(Message) -> + prompt_otp(Message) + end, + + should_authenticate => fun(Reason) -> + should_authenticate(Reason) + end, + + get_client_id => fun() -> + client_id() + end + }. + +%%==================================================================== +%% Helper functions for callbacks +%%==================================================================== + +%% @private +%% Get auth config for a specific repo from hex.config. +-spec get_repo_auth_config(RepoName, State) -> r3_hex_cli_auth:repo_auth_config() | undefined when + RepoName :: unicode:unicode_binary(), + State :: rebar_state:t(). +get_repo_auth_config(RepoName, State) -> + rebar_hex_repos:get_repo_auth_config(RepoName, State). + +%% @private +%% Get global OAuth tokens from auth config. +-spec get_global_oauth_tokens(rebar_state:t()) -> {ok, map()} | error. +get_global_oauth_tokens(State) -> + case rebar_hex_repos:get_repo_auth_config(?GLOBAL_OAUTH_KEY, State) of + #{access_token := _, expires_at := _} = Tokens -> + {ok, Tokens}; + _ -> + error + end. + +%% @private +%% Persist OAuth tokens. Scope can be 'global' or a repo name binary. +-spec persist_oauth_tokens(Scope, AccessToken, RefreshToken, ExpiresAt, State) -> ok when + Scope :: global | unicode:unicode_binary(), + AccessToken :: binary(), + RefreshToken :: binary() | undefined, + ExpiresAt :: integer(), + State :: rebar_state:t(). +persist_oauth_tokens(global, AccessToken, RefreshToken, ExpiresAt, State) -> + OAuthTokens = #{ + access_token => AccessToken, + refresh_token => RefreshToken, + expires_at => ExpiresAt + }, + rebar_hex_repos:update_repo_auth_config(OAuthTokens, ?GLOBAL_OAUTH_KEY, State), + ?DEBUG("Updated global OAuth tokens", []), + ok; +persist_oauth_tokens(RepoName, AccessToken, RefreshToken, ExpiresAt, State) -> + OAuthTokens = #{ + oauth_token => #{ + access_token => AccessToken, + refresh_token => RefreshToken, + expires_at => ExpiresAt + } + }, + rebar_hex_repos:update_repo_auth_config(OAuthTokens, RepoName, State), + ?DEBUG("Updated OAuth tokens for ~ts", [RepoName]), + ok. + +%% @private +%% Prompt user for OTP code. +-spec prompt_otp(binary()) -> {ok, binary()} | cancelled. +prompt_otp(Message) -> + ?CONSOLE("~ts", [Message]), + case io:get_line("OTP code: ") of + eof -> cancelled; + {error, _} -> cancelled; + Line -> + case string:trim(Line) of + "" -> cancelled; + Code -> {ok, list_to_binary(Code)} + end + end. + +%% @private +%% Ask user if they want to authenticate. +-spec should_authenticate(r3_hex_cli_auth:auth_prompt_reason()) -> boolean(). +should_authenticate(no_credentials) -> + ?CONSOLE("No Hex credentials found. Would you like to authenticate?", []), + prompt_yes_no(); +should_authenticate(token_refresh_failed) -> + ?CONSOLE("Hex token refresh failed. Would you like to re-authenticate?", []), + prompt_yes_no(). + +%% @private +-spec prompt_yes_no() -> boolean(). +prompt_yes_no() -> + case io:get_line("[Y/n]: ") of + eof -> false; + {error, _} -> false; + Line -> + case string:lowercase(string:trim(Line)) of + "" -> true; % Default to yes + "y" -> true; + "yes" -> true; + _ -> false + end + end. + diff --git a/apps/rebar/src/rebar_hex_repos.erl b/apps/rebar/src/rebar_hex_repos.erl index 04cdf6870..35886cf71 100644 --- a/apps/rebar/src/rebar_hex_repos.erl +++ b/apps/rebar/src/rebar_hex_repos.erl @@ -3,11 +3,14 @@ -export([from_state/2, get_repo_config/2, auth_config/1, + get_repo_auth_config/2, + update_repo_auth_config/3, remove_from_auth_config/2, update_auth_config/2, format_error/1, anon_repo_config/1, - format_repo/1 + format_repo/1, + apply_env_overrides/1 ]). -ifdef(TEST). @@ -20,16 +23,38 @@ -export_type([repo/0]). --type repo() :: #{name => unicode:unicode_binary(), - api_url => binary(), - api_key => binary(), - repo_url => binary(), - repo_key => binary(), - repo_public_key => binary(), - repo_verify => binary(), - repo_verify_origin => binary(), - mirror_of => _ % legacy field getting stripped - }. +%% repo() extends r3_hex_core:config() with rebar3-specific fields +-type repo() :: #{ + %% rebar3-specific fields + name => unicode:unicode_binary(), + repo_name => unicode:unicode_binary(), + parent => unicode:unicode_binary(), + mirror_of => term(), + %% r3_hex_core:config() fields + api_key => binary() | undefined, + api_otp => binary() | undefined, + api_organization => binary() | undefined, + api_repository => binary() | undefined, + api_url => binary(), + http_adapter => {module(), map()}, + http_etag => binary() | undefined, + http_headers => map(), + http_user_agent_fragment => binary(), + repo_key => binary() | undefined, + repo_public_key => binary(), + repo_url => binary(), + repo_organization => binary() | undefined, + repo_verify => boolean(), + repo_verify_origin => boolean(), + send_100_continue => boolean(), + tarball_max_size => pos_integer() | infinity, + tarball_max_uncompressed_size => pos_integer() | infinity, + docs_tarball_max_size => pos_integer() | infinity, + docs_tarball_max_uncompressed_size => pos_integer() | infinity, + trusted => boolean(), + oauth_exchange => boolean(), + oauth_exchange_url => binary() | undefined +}. from_state(BaseConfig, State) -> HexConfig = rebar_state:get(State, hex, []), @@ -39,22 +64,49 @@ from_state(BaseConfig, State) -> %% add base config entries that are specific to use by rebar3 and not overridable Repos1 = merge_with_base_and_auth(Repos, BaseConfig, Auth), %% merge organizations parent repo options into each oraganization repo - update_organizations(maybe_override_default_repo_url(Repos1, State)). + Repos2 = update_organizations(maybe_override_default_repo_url(Repos1, State)), + %% apply environment variable overrides to all repos + [apply_env_overrides(Repo) || Repo <- Repos2]. -spec get_repo_config(unicode:unicode_binary(), rebar_state:t() | [repo()]) - -> {ok, repo()} | error. + -> {ok, repo()}. get_repo_config(RepoName, Repos) when is_list(Repos) -> case ec_lists:find(fun(#{name := N}) -> N =:= RepoName end, Repos) of - error -> - throw(?PRV_ERROR({repo_not_found, RepoName})); {ok, RepoConfig} -> - {ok, RepoConfig} + {ok, RepoConfig}; + error -> + maybe_create_org_config(RepoName, Repos) end; get_repo_config(RepoName, State) -> Resources = rebar_state:resources(State), #{repos := Repos} = rebar_resource_v2:find_resource_state(pkg, Resources), get_repo_config(RepoName, Repos). +%% @private +%% Create a repo config for "parent:org" format repos. +%% Only succeeds if parent repo exists, otherwise throws repo_not_found. +-spec maybe_create_org_config(unicode:unicode_binary(), [repo()]) -> {ok, repo()}. +maybe_create_org_config(RepoName, Repos) -> + case rebar_string:split(RepoName, <<":">>) of + [ParentName, Org] -> + case ec_lists:find(fun(#{name := N}) -> N =:= ParentName end, Repos) of + {ok, ParentConfig} -> + OrgConfig = ParentConfig#{ + name => RepoName, + repo_name => ParentName, + repo_organization => Org, + api_organization => Org, + api_repository => Org, + parent => ParentName + }, + {ok, apply_env_overrides(OrgConfig)}; + error -> + throw(?PRV_ERROR({repo_not_found, RepoName})) + end; + _ -> + throw(?PRV_ERROR({repo_not_found, RepoName})) + end. + -spec anon_repo_config(repo()) -> #{api_url := _, name := _, repo_name => _, repo_organization => _, repo_url := _, repo_verify => _, repo_verify_origin => _, @@ -106,7 +158,7 @@ merge_repos(Repos) -> %% We set the repo_organization and api_organization to org %% for fetching and publishing private packages. update_repo_list(R#{name => Name, - repo_name => Org, + repo_name => Repo, repo_organization => Org, api_organization => Org, api_repository => Org, @@ -193,6 +245,25 @@ auth_config(State) -> ?ABORT("Error found in repos auth config (~ts) at line ~ts", [AuthFile, Reason]) end. +-spec get_repo_auth_config(unicode:unicode_binary(), rebar_state:t()) -> map() | undefined. +get_repo_auth_config(RepoName, State) -> + AuthConfig = auth_config(State), + case maps:find(RepoName, AuthConfig) of + {ok, RepoAuth} when is_map(RepoAuth) -> + RepoAuth; + _ -> + undefined + end. + +-spec update_repo_auth_config(map(), unicode:unicode_binary(), rebar_state:t()) -> ok. +update_repo_auth_config(Updates, RepoName, State) -> + ExistingRepoAuth = case get_repo_auth_config(RepoName, State) of + undefined -> #{}; + Auth -> Auth + end, + UpdatedRepoAuth = maps:merge(ExistingRepoAuth, Updates), + update_auth_config(#{RepoName => UpdatedRepoAuth}, State). + -spec remove_from_auth_config(term(), rebar_state:t()) -> ok. remove_from_auth_config(Key, State) -> Updated = maps:remove(Key, auth_config(State)), @@ -209,3 +280,103 @@ write_auth_config(Config, State) -> NewConfig = iolist_to_binary(["%% coding: utf-8", io_lib:nl(), io_lib:print(Config), ".", io_lib:nl()]), ok = file:write_file(AuthConfigFile, NewConfig, [{encoding, utf8}]). + +%% Environment variable overrides +%% These follow the same pattern as the Elixir hex package + +-spec apply_env_overrides(repo()) -> repo(). +apply_env_overrides(Config) -> + lists:foldl(fun(F, C) -> F(C) end, Config, [ + fun apply_api_key_override/1, + fun apply_api_url_override/1, + fun apply_otp_override/1, + fun apply_repos_key_override/1, + fun apply_unsafe_registry_override/1, + fun apply_no_verify_repo_origin_override/1, + fun apply_mirror_override/1 + ]). + +apply_api_key_override(Config) -> + case os:getenv("HEX_API_KEY") of + false -> Config; + "" -> Config; + ApiKey -> Config#{api_key => list_to_binary(ApiKey)} + end. + +apply_api_url_override(Config) -> + case os:getenv("HEX_API_URL") of + false -> + case os:getenv("HEX_API") of + false -> Config; + "" -> Config; + ApiUrl -> Config#{api_url => list_to_binary(ApiUrl)} + end; + "" -> Config; + ApiUrl -> Config#{api_url => list_to_binary(ApiUrl)} + end. + +apply_otp_override(Config) -> + case os:getenv("HEX_OTP") of + false -> Config; + "" -> Config; + Otp -> Config#{api_otp => list_to_binary(Otp)} + end. + +apply_repos_key_override(Config) -> + case os:getenv("HEX_REPOS_KEY") of + false -> Config; + "" -> Config; + Key -> Config#{repo_key => list_to_binary(Key)} + end. + +apply_unsafe_registry_override(Config) -> + case os:getenv("HEX_UNSAFE_REGISTRY") of + "1" -> Config#{repo_verify => false}; + "true" -> Config#{repo_verify => false}; + _ -> Config + end. + +apply_no_verify_repo_origin_override(Config) -> + case os:getenv("HEX_NO_VERIFY_REPO_ORIGIN") of + "1" -> Config#{repo_verify_origin => false}; + "true" -> Config#{repo_verify_origin => false}; + _ -> Config + end. + +apply_mirror_override(Config) -> + %% HEX_TRUSTED_MIRROR_URL takes precedence (trusted = auth credentials sent) + %% HEX_MIRROR_URL is untrusted (no auth credentials sent, no signature verification) + case os:getenv("HEX_TRUSTED_MIRROR_URL") of + false -> + case os:getenv("HEX_TRUSTED_MIRROR") of + false -> apply_untrusted_mirror_override(Config); + "" -> apply_untrusted_mirror_override(Config); + TrustedMirrorUrl -> + Config#{repo_url => list_to_binary(TrustedMirrorUrl), + trusted => true} + end; + "" -> apply_untrusted_mirror_override(Config); + TrustedMirrorUrl -> + Config#{repo_url => list_to_binary(TrustedMirrorUrl), + trusted => true} + end. + +apply_untrusted_mirror_override(Config) -> + case os:getenv("HEX_MIRROR_URL") of + false -> + case os:getenv("HEX_MIRROR") of + false -> Config; + "" -> Config; + MirrorUrl -> + Config#{repo_url => list_to_binary(MirrorUrl), + trusted => false, + repo_verify => false, + repo_verify_origin => false} + end; + "" -> Config; + MirrorUrl -> + Config#{repo_url => list_to_binary(MirrorUrl), + trusted => false, + repo_verify => false, + repo_verify_origin => false} + end. diff --git a/apps/rebar/src/rebar_packages.erl b/apps/rebar/src/rebar_packages.erl index 9f47911f6..dc4e9f990 100644 --- a/apps/rebar/src/rebar_packages.erl +++ b/apps/rebar/src/rebar_packages.erl @@ -1,6 +1,6 @@ -module(rebar_packages). --export([get/2 +-export([get/3 ,get_all_names/1 ,registry_dir/1 ,package_dir/2 @@ -29,9 +29,14 @@ format_error({missing_package, Name, Vsn}) -> format_error({missing_package, Pkg}) -> io_lib:format("Package not found in any repo: ~p", [Pkg]). --spec get(rebar_hex_repos:repo(), binary()) -> {ok, map()} | {error, term()}. -get(Config, Name) -> - try r3_hex_api_package:get(Config, Name) of +-spec get(rebar_hex_repos:repo(), binary(), rebar_state:t()) -> {ok, map()} | {error, term()}. +get(Config, Name, State) -> + handle_get_result(rebar_hex_auth:with_api(read, Config, State, [{optional, true}], fun(AuthConfig) -> + r3_hex_api_package:get(AuthConfig, Name) + end)). + +handle_get_result(Result) -> + try Result of {ok, {200, _Headers, PkgInfo}} -> {ok, PkgInfo}; {ok, {404, _, _}} -> @@ -227,7 +232,9 @@ update_package(Name, RepoConfig=#{name := Repo}, State) -> ?MODULE:verify_table(State), ?DEBUG("Getting definition for package ~ts from repo ~ts", [Name, rebar_hex_repos:format_repo(RepoConfig)]), - try r3_hex_repo:get_package(get_package_repo_config(RepoConfig), Name) of + try rebar_hex_auth:with_repo(RepoConfig, State, [], fun(AuthConfig) -> + r3_hex_repo:get_package(get_package_repo_config(AuthConfig), Name) + end) of {ok, {200, _Headers, Package}} -> #{releases := Releases} = Package, _ = insert_releases(Name, Releases, Repo, ?PACKAGE_TABLE), diff --git a/apps/rebar/src/rebar_pkg_resource.erl b/apps/rebar/src/rebar_pkg_resource.erl index d7d4f25a1..594debca7 100644 --- a/apps/rebar/src/rebar_pkg_resource.erl +++ b/apps/rebar/src/rebar_pkg_resource.erl @@ -138,11 +138,13 @@ format_error({bad_registry_checksum, Name, Vsn, Expected, Found}) -> %% {ok, Contents, NewEtag}, otherwise if some error occurred return error. %% @end %%------------------------------------------------------------------------------ --spec request(rebar_hex_repos:repo(), binary(), binary(), binary() | undefined) +-spec request(rebar_hex_repos:repo(), binary(), binary(), binary() | undefined, rebar_state:t()) -> {ok, cached} | {ok, binary(), binary()} | error. -request(Config, Name, Version, ETag) -> - Config1 = Config#{http_etag => ETag}, - try r3_hex_repo:get_tarball(Config1, Name, Version) of +request(Config, Name, Version, ETag, State) -> + try rebar_hex_auth:with_repo(Config, State, [], fun(AuthConfig) -> + Config1 = AuthConfig#{http_etag => ETag}, + r3_hex_repo:get_tarball(Config1, Name, Version) + end) of {ok, {200, #{<<"etag">> := ETag1}, Tarball}} -> {ok, Tarball, ETag1}; {ok, {304, _Headers, _}} -> @@ -210,11 +212,11 @@ store_etag_in_cache(Path, ETag) -> UpdateETag :: boolean(), Res :: ok | {unexpected_hash, integer(), integer()} | {fetch_fail, binary(), binary()} | {bad_registry_checksum, integer(), integer()} | {error, _}. -cached_download(TmpDir, CachePath, Pkg={pkg, Name, Vsn, _OldHash, _Hash, RepoConfig}, _State, ETag, +cached_download(TmpDir, CachePath, Pkg={pkg, Name, Vsn, _OldHash, _Hash, RepoConfig}, State, ETag, ETagPath, UpdateETag) -> ?DEBUG("Making request to get package ~ts from repo ~ts", [Name, rebar_hex_repos:format_repo(RepoConfig)]), - case request(RepoConfig, Name, Vsn, ETag) of + case request(RepoConfig, Name, Vsn, ETag, State) of {ok, cached} -> ?DEBUG("Version cached at ~ts is up to date, reusing it", [CachePath]), serve_from_cache(TmpDir, CachePath, Pkg); diff --git a/apps/rebar/src/rebar_prv_packages.erl b/apps/rebar/src/rebar_prv_packages.erl index a6a2c81b0..4456b48fb 100644 --- a/apps/rebar/src/rebar_prv_packages.erl +++ b/apps/rebar/src/rebar_prv_packages.erl @@ -36,7 +36,7 @@ do(State) -> Name -> Resources = rebar_state:resources(State), #{repos := Repos} = rebar_resource_v2:find_resource_state(pkg, Resources), - Results = get_package(rebar_utils:to_binary(Name), Repos), + Results = get_package(rebar_utils:to_binary(Name), Repos, State), case lists:all(fun({_, {error, not_found}}) -> true; (_) -> false end, Results) of true -> ?PRV_ERROR({not_found, Name}); @@ -46,10 +46,10 @@ do(State) -> end end. --spec get_package(binary(), [map()]) -> [{binary(), {ok, map()} | {error, term()}}]. -get_package(Name, Repos) -> +-spec get_package(binary(), [map()], rebar_state:t()) -> [{binary(), {ok, map()} | {error, term()}}]. +get_package(Name, Repos, State) -> lists:foldl(fun(RepoConfig, Acc) -> - [{maps:get(name, RepoConfig), rebar_packages:get(RepoConfig, Name)} | Acc] + [{maps:get(name, RepoConfig), rebar_packages:get(RepoConfig, Name, State)} | Acc] end, [], Repos). diff --git a/apps/rebar/src/vendored/r3_hex_api.erl b/apps/rebar/src/vendored/r3_hex_api.erl index e99cfb868..73505e47b 100644 --- a/apps/rebar/src/vendored/r3_hex_api.erl +++ b/apps/rebar/src/vendored/r3_hex_api.erl @@ -1,4 +1,4 @@ -%% Vendored from hex_core v0.15.0, do not edit manually +%% Vendored from hex_core v0.17.0, do not edit manually %% @doc %% Hex HTTP API @@ -19,7 +19,8 @@ -export_type([response/0]). -type response() :: {ok, {r3_hex_http:status(), r3_hex_http:headers(), body() | nil}} | {error, term()}. --type body() :: [body()] | #{binary() => body() | binary()}. +-type body() :: #{binary() => value()} | [#{binary() => value()}]. +-type value() :: binary() | boolean() | nil | number() | [value()] | #{binary() => value()}. %% @private get(Config, Path) -> diff --git a/apps/rebar/src/vendored/r3_hex_api_key.erl b/apps/rebar/src/vendored/r3_hex_api_key.erl index d2147a580..3e9d4d669 100644 --- a/apps/rebar/src/vendored/r3_hex_api_key.erl +++ b/apps/rebar/src/vendored/r3_hex_api_key.erl @@ -1,4 +1,4 @@ -%% Vendored from hex_core v0.15.0, do not edit manually +%% Vendored from hex_core v0.17.0, do not edit manually %% @doc %% Hex HTTP API - Keys. diff --git a/apps/rebar/src/vendored/r3_hex_api_oauth.erl b/apps/rebar/src/vendored/r3_hex_api_oauth.erl index 9b7dba5e4..cf877b33d 100644 --- a/apps/rebar/src/vendored/r3_hex_api_oauth.erl +++ b/apps/rebar/src/vendored/r3_hex_api_oauth.erl @@ -1,4 +1,4 @@ -%% Vendored from hex_core v0.15.0, do not edit manually +%% Vendored from hex_core v0.17.0, do not edit manually %% @doc %% Hex HTTP API - OAuth. @@ -6,6 +6,8 @@ -export([ device_authorization/3, device_authorization/4, + device_auth_flow/4, + device_auth_flow/5, poll_device_token/3, refresh_token/3, revoke_token/3, @@ -13,6 +15,21 @@ client_credentials_token/5 ]). +-export_type([oauth_tokens/0, device_auth_error/0]). + +-type oauth_tokens() :: #{ + access_token := binary(), + refresh_token => binary() | undefined, + expires_at := integer() +}. + +-type device_auth_error() :: + timeout + | {access_denied, Status :: non_neg_integer(), Body :: term()} + | {device_auth_failed, Status :: non_neg_integer(), Body :: term()} + | {poll_failed, Status :: non_neg_integer(), Body :: term()} + | term(). + %% @doc %% Initiates the OAuth device authorization flow. %% @@ -28,7 +45,7 @@ device_authorization(Config, ClientId, Scope) -> %% Returns device code, user code, and verification URIs for user authentication. %% %% Options: -%% * `name' - A name to identify the token (e.g., hostname of the device) +%% * `name' - A name to identify the token (defaults to the machine's hostname) %% %% Examples: %% @@ -51,17 +68,141 @@ device_authorization(Config, ClientId, Scope) -> r3_hex_api:response(). device_authorization(Config, ClientId, Scope, Opts) -> Path = <<"oauth/device_authorization">>, - Params0 = #{ - <<"client_id">> => ClientId, - <<"scope">> => Scope - }, - Params = + Name = case proplists:get_value(name, Opts) of - undefined -> Params0; - Name -> Params0#{<<"name">> => Name} + undefined -> get_hostname(); + N -> N end, + Params = #{ + <<"client_id">> => ClientId, + <<"scope">> => Scope, + <<"name">> => Name + }, r3_hex_api:post(Config, Path, Params). +%% @doc +%% Runs the complete OAuth device authorization flow. +%% +%% @see device_auth_flow/5 +%% @end +-spec device_auth_flow( + r3_hex_core:config(), + ClientId :: binary(), + Scope :: binary(), + PromptUser :: fun((VerificationUri :: binary(), UserCode :: binary()) -> ok) +) -> {ok, oauth_tokens()} | {error, device_auth_error()}. +device_auth_flow(Config, ClientId, Scope, PromptUser) -> + device_auth_flow(Config, ClientId, Scope, PromptUser, []). + +%% @doc +%% Runs the complete OAuth device authorization flow with options. +%% +%% This function handles the entire device authorization flow: +%% 1. Requests a device code from the server +%% 2. Calls `PromptUser' callback with the verification URI and user code +%% 3. Optionally opens the browser for the user (when `open_browser' is true) +%% 4. Polls the token endpoint until authorization completes or times out +%% +%% The `PromptUser' callback is responsible for displaying the verification URI +%% and user code to the user (e.g., printing to console). +%% +%% Options: +%% * `name' - A name to identify the token (defaults to the machine's hostname) +%% * `open_browser' - When `true', automatically opens the browser +%% to the verification URI. When `false' (default), only the callback is invoked. +%% +%% Returns: +%% - `{ok, Tokens}' - Authorization successful, returns access token and optional refresh token +%% - `{error, timeout}' - Device code expired before user completed authorization +%% - `{error, {access_denied, Status, Body}}' - User denied the authorization request +%% - `{error, {device_auth_failed, Status, Body}}' - Initial device authorization request failed +%% - `{error, {poll_failed, Status, Body}}' - Unexpected error during polling +%% +%% Examples: +%% +%% ``` +%% 1> Config = r3_hex_core:default_config(). +%% 2> PromptUser = fun(Uri, Code) -> +%% io:format("Visit ~s and enter code: ~s~n", [Uri, Code]) +%% end. +%% 3> r3_hex_api_oauth:device_auth_flow(Config, <<"cli">>, <<"api:write">>, PromptUser). +%% {ok, #{ +%% access_token => <<"...">>, +%% refresh_token => <<"...">>, +%% expires_at => 1234567890 +%% }} +%% ''' +%% @end +-spec device_auth_flow( + r3_hex_core:config(), + ClientId :: binary(), + Scope :: binary(), + PromptUser :: fun((VerificationUri :: binary(), UserCode :: binary()) -> ok), + proplists:proplist() +) -> {ok, oauth_tokens()} | {error, device_auth_error()}. +device_auth_flow(Config, ClientId, Scope, PromptUser, Opts) -> + case device_authorization(Config, ClientId, Scope, Opts) of + {ok, {200, _, DeviceResponse}} when is_map(DeviceResponse) -> + #{ + <<"device_code">> := DeviceCode, + <<"user_code">> := UserCode, + <<"verification_uri_complete">> := VerificationUri, + <<"expires_in">> := ExpiresIn, + <<"interval">> := IntervalSeconds + } = DeviceResponse, + ok = PromptUser(VerificationUri, UserCode), + OpenBrowser = proplists:get_value(open_browser, Opts, false), + case OpenBrowser of + true -> open_browser(VerificationUri); + false -> ok + end, + ExpiresAt = erlang:system_time(second) + ExpiresIn, + poll_for_token_loop(Config, ClientId, DeviceCode, IntervalSeconds, ExpiresAt); + {ok, {Status, _, Body}} -> + {error, {device_auth_failed, Status, Body}}; + {error, Reason} -> + {error, Reason} + end. + +%% @private +poll_for_token_loop(Config, ClientId, DeviceCode, IntervalSeconds, ExpiresAt) -> + Now = erlang:system_time(second), + case Now >= ExpiresAt of + true -> + {error, timeout}; + false -> + timer:sleep(IntervalSeconds * 1000), + case poll_device_token(Config, ClientId, DeviceCode) of + {ok, {200, _, TokenResponse}} when is_map(TokenResponse) -> + #{ + <<"access_token">> := AccessToken, + <<"expires_in">> := ExpiresIn + } = TokenResponse, + RefreshToken = maps:get(<<"refresh_token">>, TokenResponse, undefined), + TokenExpiresAt = erlang:system_time(second) + ExpiresIn, + {ok, #{ + access_token => AccessToken, + refresh_token => RefreshToken, + expires_at => TokenExpiresAt + }}; + {ok, {400, _, #{<<"error">> := <<"authorization_pending">>}}} -> + poll_for_token_loop(Config, ClientId, DeviceCode, IntervalSeconds, ExpiresAt); + {ok, {400, _, #{<<"error">> := <<"slow_down">>}}} -> + %% Increase polling interval as requested by server + poll_for_token_loop( + Config, ClientId, DeviceCode, IntervalSeconds + 5, ExpiresAt + ); + {ok, {400, _, #{<<"error">> := <<"expired_token">>}}} -> + {error, timeout}; + {ok, {Status, _, #{<<"error">> := <<"access_denied">>} = Body}} -> + {error, {access_denied, Status, Body}}; + {ok, {Status, _, Body}} -> + {error, {poll_failed, Status, Body}}; + {error, Reason} -> + {error, Reason} + end + end. + %% @doc %% Polls the OAuth token endpoint for device authorization completion. %% @@ -201,3 +342,44 @@ revoke_token(Config, ClientId, Token) -> <<"client_id">> => ClientId }, r3_hex_api:post(Config, Path, Params). + +%%==================================================================== +%% Internal functions +%%==================================================================== + +%% @private +%% Open a URL in the default browser. +%% Uses platform-specific commands: open (macOS), xdg-open (Linux), start (Windows). +-spec open_browser(binary()) -> ok. +open_browser(Url) when is_binary(Url) -> + ok = ensure_valid_http_url(Url), + UrlStr = binary_to_list(Url), + {Cmd, Args} = + case os:type() of + {unix, darwin} -> + {"open", [UrlStr]}; + {unix, _} -> + {"xdg-open", [UrlStr]}; + {win32, _} -> + {"cmd", ["/c", "start", "", UrlStr]} + end, + Port = open_port({spawn_executable, os:find_executable(Cmd)}, [{args, Args}]), + port_close(Port), + ok. + +%% @private +%% Validates that a URL uses http:// or https:// scheme. +-spec ensure_valid_http_url(binary()) -> ok. +ensure_valid_http_url(Url) when is_binary(Url) -> + case uri_string:parse(Url) of + #{scheme := <<"https">>} -> ok; + #{scheme := <<"http">>} -> ok; + _ -> throw({invalid_url, Url}) + end. + +%% @private +%% Get the hostname of the current machine. +-spec get_hostname() -> binary(). +get_hostname() -> + {ok, Hostname} = inet:gethostname(), + list_to_binary(Hostname). diff --git a/apps/rebar/src/vendored/r3_hex_api_package.erl b/apps/rebar/src/vendored/r3_hex_api_package.erl index f7850c49c..46dd92522 100644 --- a/apps/rebar/src/vendored/r3_hex_api_package.erl +++ b/apps/rebar/src/vendored/r3_hex_api_package.erl @@ -1,4 +1,4 @@ -%% Vendored from hex_core v0.15.0, do not edit manually +%% Vendored from hex_core v0.17.0, do not edit manually %% @doc %% Hex HTTP API - Packages. diff --git a/apps/rebar/src/vendored/r3_hex_api_package_owner.erl b/apps/rebar/src/vendored/r3_hex_api_package_owner.erl index a5e734323..33af12a09 100644 --- a/apps/rebar/src/vendored/r3_hex_api_package_owner.erl +++ b/apps/rebar/src/vendored/r3_hex_api_package_owner.erl @@ -1,4 +1,4 @@ -%% Vendored from hex_core v0.15.0, do not edit manually +%% Vendored from hex_core v0.17.0, do not edit manually %% @doc %% Hex HTTP API - Package Owners. diff --git a/apps/rebar/src/vendored/r3_hex_api_release.erl b/apps/rebar/src/vendored/r3_hex_api_release.erl index f6d37c34c..218fce8d6 100644 --- a/apps/rebar/src/vendored/r3_hex_api_release.erl +++ b/apps/rebar/src/vendored/r3_hex_api_release.erl @@ -1,4 +1,4 @@ -%% Vendored from hex_core v0.15.0, do not edit manually +%% Vendored from hex_core v0.17.0, do not edit manually %% @doc %% Hex HTTP API - Releases. diff --git a/apps/rebar/src/vendored/r3_hex_api_user.erl b/apps/rebar/src/vendored/r3_hex_api_user.erl index 1dc1cb60b..c4e5050d4 100644 --- a/apps/rebar/src/vendored/r3_hex_api_user.erl +++ b/apps/rebar/src/vendored/r3_hex_api_user.erl @@ -1,4 +1,4 @@ -%% Vendored from hex_core v0.15.0, do not edit manually +%% Vendored from hex_core v0.17.0, do not edit manually %% @doc %% Hex HTTP API - Users. diff --git a/apps/rebar/src/vendored/r3_hex_cli_auth.erl b/apps/rebar/src/vendored/r3_hex_cli_auth.erl new file mode 100644 index 000000000..d7dd371a7 --- /dev/null +++ b/apps/rebar/src/vendored/r3_hex_cli_auth.erl @@ -0,0 +1,724 @@ +%% Vendored from hex_core v0.17.0, do not edit manually + +%% @doc +%% Authentication handling with callback functions for build-tool-specific operations. +%% +%% This module provides generic authentication handling that allows both rebar3 +%% and Elixir Hex (and future build tools) to share the common auth logic while +%% customizing prompting, persistence, and configuration retrieval. +%% +%% == Callbacks == +%% +%% The caller provides a callbacks map with these functions (all required): +%% +%% ``` +%% #{ +%% %% Auth configuration for a specific repo +%% get_auth_config => fun((RepoName :: binary()) -> +%% #{api_key => binary(), +%% auth_key => binary(), +%% oauth_exchange => boolean(), +%% oauth_exchange_url => binary()} | undefined), +%% +%% %% Global OAuth tokens - storage and retrieval +%% get_oauth_tokens => fun(() -> {ok, #{access_token := binary(), +%% refresh_token => binary(), +%% expires_at := integer()}} | error), +%% persist_oauth_tokens => fun((Scope :: global | binary(), +%% AccessToken :: binary(), +%% RefreshToken :: binary() | undefined, +%% ExpiresAt :: integer()) -> ok), +%% +%% %% User interaction +%% prompt_otp => fun((Message :: binary()) -> {ok, OtpCode :: binary()} | cancelled), +%% should_authenticate => fun((Reason :: no_credentials | token_refresh_failed) -> boolean()), +%% +%% %% OAuth client configuration +%% get_client_id => fun(() -> binary()) +%% } +%% ''' +%% +%% == Auth Resolution Order == +%% +%% For API calls: +%%
    +%%
  1. Per-repo `api_key' from config (with optional OAuth exchange for hex.pm)
  2. +%%
  3. Parent repo `api_key' (for "hexpm:org" organizations)
  4. +%%
  5. Global OAuth token (refreshed if expired)
  6. +%%
  7. Device auth flow (for write operations only)
  8. +%%
+%% +%% For repo calls: +%%
    +%%
  1. Per-repo `auth_key' with optional OAuth exchange (default true for hex.pm)
  2. +%%
  3. Parent repo `auth_key'
  4. +%%
  5. Global OAuth token
  6. +%%
+%% +%% == OAuth Exchange == +%% +%% For hex.pm URLs, `api_key' and `auth_key' are exchanged for short-lived OAuth +%% tokens via the client credentials grant. This behavior can be controlled per-repo +%% via the `oauth_exchange' option in the repo config (defaults to `true' for hex.pm). +%% +%% == Auth Context == +%% +%% Internally, authentication resolution tracks context via `auth_context()': +%% +%% +%% == Token Format == +%% +%% OAuth access tokens are automatically prefixed with `<<"Bearer ">>' when used +%% as `api_key' or `repo_key' in the config. +-module(r3_hex_cli_auth). + +-export([ + with_api/4, + with_api/5, + with_repo/3, + with_repo/4, + resolve_api_auth/3, + resolve_repo_auth/2 +]). + +-export_type([ + callbacks/0, + permission/0, + auth_error/0, + auth_context/0, + repo_auth_config/0, + auth_prompt_reason/0, + opts/0 +]). + +%% 5 minute buffer before expiry +-define(EXPIRY_BUFFER_SECONDS, 300). + +%% Maximum OTP retry attempts +-define(MAX_OTP_RETRIES, 3). + +-type permission() :: read | write. + +-type callbacks() :: #{ + get_auth_config := fun((RepoName :: binary()) -> repo_auth_config() | undefined), + get_oauth_tokens := fun(() -> {ok, oauth_tokens()} | error), + persist_oauth_tokens := fun( + ( + Scope :: global | binary(), + AccessToken :: binary(), + RefreshToken :: binary() | undefined, + ExpiresAt :: integer() + ) -> ok + ), + prompt_otp := fun((Message :: binary()) -> {ok, OtpCode :: binary()} | cancelled), + should_authenticate := fun((Reason :: auth_prompt_reason()) -> boolean()), + get_client_id := fun(() -> binary()) +}. + +-type auth_prompt_reason() :: + no_credentials + | token_refresh_failed. + +-type repo_auth_config() :: #{ + api_key => binary(), + repo_key => binary(), + auth_key => binary(), + oauth_token => oauth_tokens() +}. + +-type oauth_tokens() :: #{ + access_token := binary(), + refresh_token => binary(), + expires_at := integer() +}. + +-type auth_error() :: + {auth_error, no_credentials} + | {auth_error, auth_declined} + | {auth_error, otp_cancelled} + | {auth_error, otp_max_retries} + | {auth_error, token_refresh_failed} + | {auth_error, device_auth_timeout} + | {auth_error, device_auth_denied} + | {auth_error, oauth_exchange_failed} + | {auth_error, term()}. + +-type auth_context() :: #{ + source => env | config | oauth, + has_refresh_token => boolean() +}. + +-type opts() :: [ + {optional, boolean()} + | {auth_inline, boolean()} + | {oauth_open_browser, boolean()} +]. + +%%==================================================================== +%% API functions +%%==================================================================== + +%% @doc +%% Execute a function with API authentication. +%% +%% Equivalent to `with_api(Callbacks, Permission, Config, Fun, [])'. +%% +%% @see with_api/5 +-spec with_api(callbacks(), permission(), r3_hex_core:config(), fun((r3_hex_core:config()) -> Result)) -> + Result | {error, auth_error()} +when + Result :: term(). +with_api(Callbacks, Permission, BaseConfig, Fun) -> + with_api(Callbacks, Permission, BaseConfig, Fun, []). + +%% @doc +%% Execute a function with API authentication. +%% +%% Resolves credentials in this order: +%%
    +%%
  1. Per-repo `api_key' from config (with optional OAuth exchange for hex.pm)
  2. +%%
  3. Parent repo `api_key' (for "hexpm:org" organizations)
  4. +%%
  5. Global OAuth token (refreshed if expired)
  6. +%%
  7. Device auth flow (when `should_authenticate' callback returns true)
  8. +%%
+%% +%% On 401 responses, handles OTP prompts and token refresh automatically. +%% +%% The repository name is taken from the config (`repo_name' or `repo_organization'). +%% +%% Options: +%% +%% +%% Example: +%% ``` +%% r3_hex_cli_auth:with_api(Callbacks, write, Config, fun(C) -> +%% r3_hex_api_release:publish(C, Tarball) +%% end, [{optional, false}, {auth_inline, true}]). +%% ''' +-spec with_api( + callbacks(), + permission(), + r3_hex_core:config(), + fun((r3_hex_core:config()) -> Result), + opts() +) -> + Result | {error, auth_error()} +when + Result :: term(). +with_api(Callbacks, Permission, BaseConfig, Fun, Opts) -> + Optional = proplists:get_value(optional, Opts, false), + AuthInline = proplists:get_value(auth_inline, Opts, true), + case resolve_api_auth(Callbacks, Permission, BaseConfig) of + {ok, ApiKey, AuthContext} -> + Config = BaseConfig#{api_key => ApiKey}, + execute_with_retry(Callbacks, Config, Fun, AuthContext, 0, undefined, Opts); + {error, no_auth} when Optional =:= true -> + %% Auth is optional, try without credentials first + execute_optional_with_retry(Callbacks, BaseConfig, Fun, Opts); + {error, no_auth} when AuthInline =:= true -> + %% No auth found, ask user if they want to authenticate + maybe_authenticate_and_retry(Callbacks, BaseConfig, Fun, no_credentials, Opts); + {error, no_auth} -> + %% auth_inline is false, just return error + {error, {auth_error, no_credentials}}; + {error, _} = Error -> + Error + end. + +%% @doc +%% Execute a function with repository authentication. +%% +%% Equivalent to `with_repo(Callbacks, Config, Fun, [])'. +%% +%% @see with_repo/4 +-spec with_repo(callbacks(), r3_hex_core:config(), fun((r3_hex_core:config()) -> Result)) -> + Result | {error, auth_error()} +when + Result :: term(). +with_repo(Callbacks, BaseConfig, Fun) -> + with_repo(Callbacks, BaseConfig, Fun, []). + +%% @doc +%% Execute a function with repository authentication. +%% +%% Resolves credentials in this order: +%%
    +%%
  1. `repo_key' in config - passthrough
  2. +%%
  3. `repo_key' from `get_auth_config' callback - passthrough
  4. +%%
  5. `auth_key' from `get_auth_config' when `trusted' is true and `oauth_exchange' is true - exchange for OAuth token
  6. +%%
  7. `auth_key' from `get_auth_config' when `trusted' is true - use directly
  8. +%%
  9. Global OAuth token from `get_oauth_tokens' callback
  10. +%%
  11. No auth when `optional' is true (with retry on 401)
  12. +%%
  13. Prompt via `should_authenticate' when `auth_inline' is true
  14. +%%
+%% +%% The repository name is taken from the config (`repo_name' or `repo_organization'). +%% +%% Options: +%% +%% +%% Example: +%% ``` +%% r3_hex_cli_auth:with_repo(Callbacks, Config, fun(C) -> +%% r3_hex_repo:get_tarball(C, <<"ecto">>, <<"3.0.0">>) +%% end). +%% ''' +-spec with_repo( + callbacks(), r3_hex_core:config(), fun((r3_hex_core:config()) -> Result), opts() +) -> + Result | {error, auth_error()} +when + Result :: term(). +with_repo(Callbacks, BaseConfig, Fun, Opts) -> + Optional = proplists:get_value(optional, Opts, true), + AuthInline = proplists:get_value(auth_inline, Opts, false), + case resolve_repo_auth(Callbacks, BaseConfig) of + {ok, RepoKey, _AuthContext} when is_binary(RepoKey) -> + Config = BaseConfig#{repo_key => RepoKey}, + Fun(Config); + no_auth when Optional =:= true -> + %% Auth is optional, try without credentials first + execute_optional_with_retry(Callbacks, BaseConfig, Fun, Opts); + no_auth when AuthInline =:= true -> + %% No auth found, ask user if they want to authenticate + maybe_authenticate_and_retry(Callbacks, BaseConfig, Fun, no_credentials, Opts); + no_auth -> + %% auth_inline is false, return error + {error, {auth_error, no_credentials}}; + {error, {auth_error, token_refresh_failed}} when Optional =:= true -> + %% Token refresh failed but auth is optional, fall back to no credentials + execute_optional_with_retry(Callbacks, BaseConfig, Fun, Opts); + {error, _} = Error -> + Error + end. + +%% @private +%% Extract repository name from config. +-spec repo_name(r3_hex_core:config()) -> binary(). +repo_name(#{repo_name := Name, repo_organization := Org}) when is_binary(Name) and is_binary(Org) -> + <>; +repo_name(#{repo_name := Name}) when is_binary(Name) -> Name; +repo_name(_) -> + <<"hexpm">>. + +%% @private +%% Ask user if they want to authenticate, and if yes, initiate device auth. +maybe_authenticate_and_retry(Callbacks, BaseConfig, Fun, Reason, Opts) -> + case call_callback(Callbacks, should_authenticate, [Reason]) of + true -> + case device_auth(Callbacks, BaseConfig, <<"api repositories">>, Opts) of + {ok, #{access_token := Token}} -> + BearerToken = <<"Bearer ", Token/binary>>, + Config = BaseConfig#{api_key => BearerToken}, + AuthContext = #{source => oauth, has_refresh_token => true}, + execute_with_retry(Callbacks, Config, Fun, AuthContext, 0, undefined, Opts); + {error, _} = Error -> + Error + end; + false -> + {error, {auth_error, auth_declined}} + end. + +%% @private +%% Execute function without auth, but retry with auth if we get a 401. +execute_optional_with_retry(Callbacks, BaseConfig, Fun, Opts) -> + AuthInline = proplists:get_value(auth_inline, Opts, true), + case Fun(BaseConfig) of + {ok, {401, _Headers, _Body}} when AuthInline =:= true -> + %% Got 401, need auth - ask user if they want to authenticate + maybe_authenticate_and_retry(Callbacks, BaseConfig, Fun, no_credentials, Opts); + {ok, {401, _Headers, _Body}} -> + %% Got 401 but auth_inline is false, return error + {error, {auth_error, no_credentials}}; + Other -> + Other + end. + +%%==================================================================== +%% Internal functions - Device Auth +%%==================================================================== + +%% @private +%% Initiate OAuth device authorization flow. +%% Prompts user, optionally opens the browser for user authentication, +%% polls for token completion, and persists tokens via callback on success. +-spec device_auth(callbacks(), r3_hex_core:config(), binary(), opts()) -> + {ok, oauth_tokens()} | {error, auth_error()}. +device_auth(Callbacks, Config, Scope, Opts) -> + ClientId = call_callback(Callbacks, get_client_id, []), + OpenBrowser = proplists:get_value(oauth_open_browser, Opts, true), + PromptUser = fun(VerificationUri, UserCode) -> + io:format("Open ~ts in your browser and enter code: ~ts~n", [VerificationUri, UserCode]) + end, + FlowOpts = [{open_browser, OpenBrowser}], + case r3_hex_api_oauth:device_auth_flow(Config, ClientId, Scope, PromptUser, FlowOpts) of + {ok, #{access_token := AccessToken, refresh_token := RefreshToken, expires_at := ExpiresAt}} -> + ok = call_callback(Callbacks, persist_oauth_tokens, [ + global, AccessToken, RefreshToken, ExpiresAt + ]), + {ok, #{ + access_token => AccessToken, + refresh_token => RefreshToken, + expires_at => ExpiresAt + }}; + {error, timeout} -> + {error, {auth_error, device_auth_timeout}}; + {error, {access_denied, _Status, _Body}} -> + {error, {auth_error, device_auth_denied}}; + {error, {device_auth_failed, _Status, _Body} = Reason} -> + {error, {auth_error, Reason}}; + {error, {poll_failed, _Status, _Body} = Reason} -> + {error, {auth_error, Reason}}; + {error, Reason} -> + {error, {auth_error, Reason}} + end. + +%% @private +%% Check if a token is expired (within 5 minute buffer). +-spec is_token_expired(integer()) -> boolean(). +is_token_expired(ExpiresAt) -> + Now = erlang:system_time(second), + ExpiresAt - Now < ?EXPIRY_BUFFER_SECONDS. + +%%==================================================================== +%% Internal functions - Auth Resolution +%%==================================================================== + +%% @private +-spec resolve_api_auth(callbacks(), permission(), r3_hex_core:config()) -> + {ok, binary(), auth_context()} | {error, no_auth} | {error, auth_error()}. +resolve_api_auth(_Callbacks, _Permission, #{api_key := ApiKey}) when is_binary(ApiKey) -> + %% api_key already in config, pass through directly + {ok, ApiKey, #{source => config, has_refresh_token => false}}; +resolve_api_auth(Callbacks, _Permission, Config) -> + RepoName = repo_name(Config), + %% 1. Check per-repo api_key + case call_callback(Callbacks, get_auth_config, [RepoName]) of + #{api_key := ApiKey} when is_binary(ApiKey) -> + {ok, ApiKey, #{source => config, has_refresh_token => false}}; + _ -> + %% 2. Check parent repo (for "hexpm:org" organizations) + case get_parent_repo_key(Callbacks, RepoName, api_key) of + {ok, ApiKey} -> + {ok, ApiKey, #{source => config, has_refresh_token => false}}; + error -> + %% 3. Try global OAuth token + resolve_oauth_token_with_context(Callbacks, Config) + end + end. + +%% @private +%% Resolve repo auth credentials in this order: +%% 0. repo_key in config => passthrough +%% 1. repo_key from get_auth_config => passthrough +%% 2. trusted + auth_key + oauth_exchange => exchange for OAuth token +%% 3. trusted + auth_key => use directly +%% 4. trusted + global OAuth tokens => use those +%% 5. Fallthrough to no_auth (handled by with_repo/4 for optional/auth_inline) +-spec resolve_repo_auth(callbacks(), r3_hex_core:config()) -> + {ok, binary(), auth_context()} | no_auth | {error, auth_error()}. +resolve_repo_auth(_Callbacks, #{repo_key := RepoKey}) when is_binary(RepoKey) -> + %% repo_key already in config, pass through directly + {ok, RepoKey, #{source => config, has_refresh_token => false}}; +resolve_repo_auth(Callbacks, Config) -> + RepoName = repo_name(Config), + global:trans( + {{?MODULE, repo}, RepoName}, + fun() -> + do_resolve_repo_auth(Callbacks, RepoName, RepoName, Config) + end, + [], + infinity + ). + +do_resolve_repo_auth(Callbacks, RepoName, LookupRepo, Config) -> + Trusted = maps:get(trusted, Config, false), + OAuthExchange = maps:get(oauth_exchange, Config, false), + case call_callback(Callbacks, get_auth_config, [LookupRepo]) of + #{repo_key := RepoKey} when is_binary(RepoKey) -> + %% 1. repo_key from get_auth_config => passthrough + {ok, RepoKey, #{source => config, has_refresh_token => false}}; + #{oauth_token := OAuthToken, auth_key := AuthKey} when + is_binary(AuthKey) and OAuthExchange, Trusted + -> + %% 2. trusted + oauth_token + auth_key + oauth_exchange => use/refresh existing token + resolve_repo_oauth_token(Callbacks, RepoName, Config, AuthKey, OAuthToken); + #{auth_key := AuthKey} when is_binary(AuthKey) and OAuthExchange, Trusted -> + %% 3. trusted + auth_key + oauth_exchange => exchange for new OAuth token + exchange_for_oauth_token(Callbacks, RepoName, Config, AuthKey, <<"repositories">>); + #{auth_key := AuthKey} when is_binary(AuthKey), Trusted -> + %% 4. trusted + auth_key => use directly + {ok, AuthKey, #{source => config, has_refresh_token => false}}; + _ when Trusted -> + %% 5. Check parent repo (for "hexpm:org" organizations) + case binary:split(LookupRepo, <<":">>) of + [ParentName, _OrgName] -> + do_resolve_repo_auth(Callbacks, RepoName, ParentName, Config); + _ -> + %% 6. trusted + global OAuth tokens => use those + resolve_global_oauth_for_repo(Callbacks, Config) + end; + _ -> + %% 7. Not trusted, no auth + no_auth + end. + +%% @private +resolve_global_oauth_for_repo(Callbacks, Config) -> + case resolve_oauth_token_with_context(Callbacks, Config) of + {ok, Token, AuthContext} -> + {ok, Token, AuthContext}; + {error, no_auth} -> + no_auth; + {error, _} = Error -> + Error + end. + +%% @private +%% Resolve repo OAuth token: use if valid, re-exchange if expiring. +resolve_repo_oauth_token(Callbacks, RepoName, Config, AuthKey, #{ + access_token := AccessToken, expires_at := ExpiresAt +}) -> + case is_token_expired(ExpiresAt) of + false -> + %% Token is still valid, use it + BearerToken = <<"Bearer ", AccessToken/binary>>, + {ok, BearerToken, #{source => oauth, has_refresh_token => false}}; + true -> + %% Token expired, do a new exchange + exchange_for_oauth_token(Callbacks, RepoName, Config, AuthKey, <<"repositories">>) + end. + +%% @private +%% Exchange api_key/auth_key for OAuth token via client credentials grant. +%% Persists the token with the repo name for per-repo token storage. +exchange_for_oauth_token(Callbacks, RepoName, Config, AuthKey, Scope) -> + ClientId = call_callback(Callbacks, get_client_id, []), + ExchangeConfig = + case maps:get(oauth_exchange_url, Config, undefined) of + undefined -> Config; + OAuthUrl -> Config#{api_url => OAuthUrl} + end, + case r3_hex_api_oauth:client_credentials_token(ExchangeConfig, ClientId, AuthKey, Scope) of + {ok, {200, _, #{<<"access_token">> := AccessToken, <<"expires_in">> := ExpiresIn}}} -> + ExpiresAt = erlang:system_time(second) + ExpiresIn, + ok = call_callback(Callbacks, persist_oauth_tokens, [ + RepoName, AccessToken, undefined, ExpiresAt + ]), + BearerToken = <<"Bearer ", AccessToken/binary>>, + {ok, BearerToken, #{source => oauth, has_refresh_token => false}}; + {ok, {_Status, _, _Body}} -> + {error, {auth_error, oauth_exchange_failed}}; + {error, _} -> + {error, {auth_error, oauth_exchange_failed}} + end. + +%% @private +get_parent_repo_key(Callbacks, RepoName, KeyType) -> + case binary:split(RepoName, <<":">>) of + [ParentName, _OrgName] -> + case call_callback(Callbacks, get_auth_config, [ParentName]) of + #{KeyType := Key} when is_binary(Key) -> + {ok, Key}; + _ -> + error + end; + _ -> + error + end. + +%% @private +%% Resolve OAuth token with global lock to prevent concurrent refresh attempts. +resolve_oauth_token_with_context(Callbacks, Config) -> + global:trans( + {{?MODULE, token_refresh}, self()}, + fun() -> + do_resolve_oauth_token_with_context(Callbacks, Config) + end, + [], + infinity + ). + +%% @private +do_resolve_oauth_token_with_context(Callbacks, Config) -> + case call_callback(Callbacks, get_oauth_tokens, []) of + {ok, #{access_token := AccessToken, expires_at := ExpiresAt} = Tokens} -> + HasRefreshToken = + maps:is_key(refresh_token, Tokens) andalso + is_binary(maps:get(refresh_token, Tokens)), + case is_token_expired(ExpiresAt) of + true -> + maybe_refresh_token_with_context(Callbacks, Config, Tokens); + false -> + BearerToken = <<"Bearer ", AccessToken/binary>>, + {ok, BearerToken, #{source => oauth, has_refresh_token => HasRefreshToken}} + end; + error -> + {error, no_auth} + end. + +%% @private +maybe_refresh_token_with_context(Callbacks, Config, #{refresh_token := RefreshToken}) when + is_binary(RefreshToken) +-> + ClientId = call_callback(Callbacks, get_client_id, []), + case r3_hex_api_oauth:refresh_token(Config, ClientId, RefreshToken) of + {ok, {200, _, TokenResponse}} when is_map(TokenResponse) -> + #{ + <<"access_token">> := NewAccessToken, + <<"expires_in">> := ExpiresIn + } = TokenResponse, + NewRefreshToken = maps:get(<<"refresh_token">>, TokenResponse, RefreshToken), + ExpiresAt = erlang:system_time(second) + ExpiresIn, + ok = call_callback(Callbacks, persist_oauth_tokens, [ + global, NewAccessToken, NewRefreshToken, ExpiresAt + ]), + BearerToken = <<"Bearer ", NewAccessToken/binary>>, + HasRefreshToken = is_binary(NewRefreshToken), + {ok, BearerToken, #{source => oauth, has_refresh_token => HasRefreshToken}}; + {ok, {_Status, _, _Body}} -> + {error, {auth_error, token_refresh_failed}}; + {error, _Reason} -> + {error, {auth_error, token_refresh_failed}} + end; +maybe_refresh_token_with_context(_Callbacks, _Config, _Tokens) -> + {error, {auth_error, token_refresh_failed}}. + +%%==================================================================== +%% Internal functions - Retry Logic +%%==================================================================== + +%% @private +execute_with_retry(Callbacks, Config, Fun, AuthContext, OtpRetries, LastOtpError, Opts) -> + case Fun(Config) of + {error, otp_required} -> + handle_otp_retry( + Callbacks, Config, Fun, AuthContext, OtpRetries, <<"Enter OTP code:">>, Opts + ); + {error, invalid_totp} -> + handle_otp_retry( + Callbacks, + Config, + Fun, + AuthContext, + OtpRetries, + <<"Invalid OTP code. Please try again:">>, + Opts + ); + {ok, {401, Headers, _Body}} = Response -> + case detect_auth_error(Headers) of + otp_required -> + handle_otp_retry( + Callbacks, Config, Fun, AuthContext, OtpRetries, <<"Enter OTP code:">>, Opts + ); + invalid_totp -> + Msg = + case LastOtpError of + invalid_totp -> <<"Invalid OTP code. Please try again:">>; + _ -> <<"Enter OTP code:">> + end, + handle_otp_retry(Callbacks, Config, Fun, AuthContext, OtpRetries, Msg, Opts); + token_expired -> + handle_token_refresh_retry(Callbacks, Config, Fun, AuthContext, Opts); + none -> + Response + end; + Other -> + Other + end. + +%% @private +handle_otp_retry(_Callbacks, _Config, _Fun, _AuthContext, OtpRetries, _Message, _Opts) when + OtpRetries >= ?MAX_OTP_RETRIES +-> + {error, {auth_error, otp_max_retries}}; +handle_otp_retry(Callbacks, Config, Fun, AuthContext, OtpRetries, Message, Opts) -> + case call_callback(Callbacks, prompt_otp, [Message]) of + {ok, OtpCode} -> + NewConfig = Config#{api_otp => OtpCode}, + execute_with_retry( + Callbacks, NewConfig, Fun, AuthContext, OtpRetries + 1, invalid_totp, Opts + ); + cancelled -> + {error, {auth_error, otp_cancelled}} + end. + +%% @private +handle_token_refresh_retry(Callbacks, Config, Fun, AuthContext, Opts) -> + %% Only attempt refresh if we have a refresh token + case maps:get(has_refresh_token, AuthContext, false) of + true -> + case resolve_oauth_token_with_context(Callbacks, Config) of + {ok, NewBearerToken, NewAuthContext} -> + NewConfig = Config#{api_key => NewBearerToken}, + execute_with_retry( + Callbacks, NewConfig, Fun, NewAuthContext, 0, undefined, Opts + ); + {error, _} -> + maybe_reauthenticate(Callbacks, Config, Fun, Opts) + end; + false -> + maybe_reauthenticate(Callbacks, Config, Fun, Opts) + end. + +%% @private +%% After token refresh failure, prompt the user to re-authenticate via device auth +%% (only when auth_inline is true). Mirrors Hex.OAuth.reauthenticate/1. +maybe_reauthenticate(Callbacks, Config, Fun, Opts) -> + AuthInline = proplists:get_value(auth_inline, Opts, true), + case AuthInline of + true -> + maybe_authenticate_and_retry(Callbacks, Config, Fun, token_refresh_failed, Opts); + false -> + {error, {auth_error, token_refresh_failed}} + end. + +%% @private +-spec detect_auth_error(r3_hex_http:headers()) -> otp_required | invalid_totp | token_expired | none. +detect_auth_error(Headers) -> + case maps:get(<<"www-authenticate">>, Headers, undefined) of + undefined -> + none; + Value -> + parse_www_authenticate(Value) + end. + +%% @private +parse_www_authenticate(Value) when is_binary(Value) -> + case Value of + <<"Bearer realm=\"hex\", error=\"totp_required\"", _/binary>> -> + otp_required; + <<"Bearer realm=\"hex\", error=\"invalid_totp\"", _/binary>> -> + invalid_totp; + <<"Bearer realm=\"hex\", error=\"token_expired\"", _/binary>> -> + token_expired; + _ -> + none + end. + +%%==================================================================== +%% Internal functions - Utilities +%%==================================================================== + +%% @private +call_callback(Callbacks, Name, Args) -> + Fun = maps:get(Name, Callbacks), + erlang:apply(Fun, Args). diff --git a/apps/rebar/src/vendored/r3_hex_core.erl b/apps/rebar/src/vendored/r3_hex_core.erl index 4bcf7f618..81648c5c4 100644 --- a/apps/rebar/src/vendored/r3_hex_core.erl +++ b/apps/rebar/src/vendored/r3_hex_core.erl @@ -1,4 +1,4 @@ -%% Vendored from hex_core v0.15.0, do not edit manually +%% Vendored from hex_core v0.17.0, do not edit manually %% @doc %% `hex_core' entrypoint module. @@ -68,11 +68,21 @@ %% * `tarball_max_uncompressed_size' - Maximum size of uncompressed package tarball, defaults to %% `134_217_728' (128 MiB). Set to `infinity' to not enforce the limit. %% +%% * `tarball_files_root' - Root directory for source files when creating tarballs. +%% Filesystem source paths must resolve inside this root after following symlinks. +%% Relative source paths are resolved from this root and absolute source paths must be +%% inside it (default: `"."'). +%% %% * `docs_tarball_max_size' - Maximum size of docs tarball, defaults to %% `16_777_216' (16 MiB). Set to `infinity' to not enforce the limit. %% %% * `docs_tarball_max_uncompressed_size' - Maximum size of uncompressed docs tarball, defaults to %% `134_217_728' (128 MiB). Set to `infinity' to not enforce the limit. +%% +%% * `metadata_fields' - Either `all' or a list of metadata.config keys (binaries) to read. +%% When set to a list, the metadata decoder streams past unrequested fields without +%% buffering their tokens, which keeps peak memory bounded for packages with very +%% large fields like `<<"files">>'. Defaults to `all'. -module(r3_hex_core). -export([default_config/0]). @@ -110,10 +120,15 @@ repo_verify => boolean(), repo_verify_origin => boolean(), send_100_continue => boolean(), + tarball_files_root => file:filename(), tarball_max_size => pos_integer() | infinity, tarball_max_uncompressed_size => pos_integer() | infinity, docs_tarball_max_size => pos_integer() | infinity, - docs_tarball_max_uncompressed_size => pos_integer() | infinity + docs_tarball_max_uncompressed_size => pos_integer() | infinity, + metadata_fields => all | [binary()], + trusted => boolean(), + oauth_exchange => boolean(), + oauth_exchange_url => binary() | undefined }. -spec default_config() -> config(). @@ -136,8 +151,13 @@ default_config() -> repo_verify => true, repo_verify_origin => true, send_100_continue => true, + tarball_files_root => ".", tarball_max_size => 16 * 1024 * 1024, tarball_max_uncompressed_size => 128 * 1024 * 1024, docs_tarball_max_size => 16 * 1024 * 1024, - docs_tarball_max_uncompressed_size => 128 * 1024 * 1024 + docs_tarball_max_uncompressed_size => 128 * 1024 * 1024, + metadata_fields => all, + trusted => true, + oauth_exchange => true, + oauth_exchange_url => undefined }. diff --git a/apps/rebar/src/vendored/r3_hex_core.hrl b/apps/rebar/src/vendored/r3_hex_core.hrl index 43bc6f9f9..47b0e8bcb 100644 --- a/apps/rebar/src/vendored/r3_hex_core.hrl +++ b/apps/rebar/src/vendored/r3_hex_core.hrl @@ -1,3 +1,3 @@ -%% Vendored from hex_core v0.15.0, do not edit manually +%% Vendored from hex_core v0.17.0, do not edit manually --define(HEX_CORE_VERSION, "0.15.0"). +-define(HEX_CORE_VERSION, "0.17.0"). diff --git a/apps/rebar/src/vendored/r3_hex_erl_tar.erl b/apps/rebar/src/vendored/r3_hex_erl_tar.erl index fd8bf486e..b4a8e7ebe 100644 --- a/apps/rebar/src/vendored/r3_hex_erl_tar.erl +++ b/apps/rebar/src/vendored/r3_hex_erl_tar.erl @@ -1,4 +1,4 @@ -%% Vendored from hex_core v0.15.0, do not edit manually +%% Vendored from hex_core v0.17.0, do not edit manually %% This file is a copy of erl_tar.erl from OTP with the following modifications: %% 1. Module renamed from erl_tar to r3_hex_erl_tar diff --git a/apps/rebar/src/vendored/r3_hex_erl_tar.hrl b/apps/rebar/src/vendored/r3_hex_erl_tar.hrl index 7b617350b..dc1b81276 100644 --- a/apps/rebar/src/vendored/r3_hex_erl_tar.hrl +++ b/apps/rebar/src/vendored/r3_hex_erl_tar.hrl @@ -1,4 +1,4 @@ -%% Vendored from hex_core v0.15.0, do not edit manually +%% Vendored from hex_core v0.17.0, do not edit manually %% This file is a copy of erl_tar.hrl from OTP with the following modifications: %% 1. Added chunk_size field to #read_opts{} for streaming extraction to disk diff --git a/apps/rebar/src/vendored/r3_hex_http.erl b/apps/rebar/src/vendored/r3_hex_http.erl index 3b53974ee..6adf34c7e 100644 --- a/apps/rebar/src/vendored/r3_hex_http.erl +++ b/apps/rebar/src/vendored/r3_hex_http.erl @@ -1,4 +1,4 @@ -%% Vendored from hex_core v0.15.0, do not edit manually +%% Vendored from hex_core v0.17.0, do not edit manually %% @doc %% HTTP contract. diff --git a/apps/rebar/src/vendored/r3_hex_http_httpc.erl b/apps/rebar/src/vendored/r3_hex_http_httpc.erl index 335776e43..1eb8e960c 100644 --- a/apps/rebar/src/vendored/r3_hex_http_httpc.erl +++ b/apps/rebar/src/vendored/r3_hex_http_httpc.erl @@ -1,4 +1,4 @@ -%% Vendored from hex_core v0.15.0, do not edit manually +%% Vendored from hex_core v0.17.0, do not edit manually %% @doc %% httpc-based implementation of {@link r3_hex_http} contract. @@ -41,15 +41,12 @@ request_to_file(Method, URI, ReqHeaders, Body, Filename, AdapterConfig) when is_ Method, Request, HTTPOptions, - [{stream, unicode:characters_to_list(Filename)}], + [{sync, false}, {stream, self}], Profile ) of - {ok, saved_to_file} -> - {ok, {200, #{}}}; - {ok, {{_, StatusCode, _}, RespHeaders, _RespBody}} -> - RespHeaders2 = load_headers(RespHeaders), - {ok, {StatusCode, RespHeaders2}}; + {ok, RequestId} -> + stream_to_file(RequestId, Filename); {error, Reason} -> {error, Reason} end. @@ -58,6 +55,39 @@ request_to_file(Method, URI, ReqHeaders, Body, Filename, AdapterConfig) when is_ %% Internal functions %%==================================================================== +%% @private +%% httpc streams 200/206 responses as messages and returns non-2xx as +%% a normal response tuple. stream_start includes the response headers. +stream_to_file(RequestId, Filename) -> + receive + {http, {RequestId, stream_start, Headers}} -> + {ok, File} = file:open(Filename, [write, binary]), + case stream_body(RequestId, File) of + ok -> + ok = file:close(File), + {ok, {200, load_headers(Headers)}}; + {error, Reason} -> + ok = file:close(File), + {error, Reason} + end; + {http, {RequestId, {{_, StatusCode, _}, RespHeaders, _RespBody}}} -> + {ok, {StatusCode, load_headers(RespHeaders)}}; + {http, {RequestId, {error, Reason}}} -> + {error, Reason} + end. + +%% @private +stream_body(RequestId, File) -> + receive + {http, {RequestId, stream, BinBodyPart}} -> + ok = file:write(File, BinBodyPart), + stream_body(RequestId, File); + {http, {RequestId, stream_end, _Headers}} -> + ok; + {http, {RequestId, {error, Reason}}} -> + {error, Reason} + end. + %% @private http_options(URI, AdapterConfig) -> HTTPOptions0 = maps:get(http_options, AdapterConfig, []), diff --git a/apps/rebar/src/vendored/r3_hex_licenses.erl b/apps/rebar/src/vendored/r3_hex_licenses.erl index 2abe803b6..6e429e9f8 100644 --- a/apps/rebar/src/vendored/r3_hex_licenses.erl +++ b/apps/rebar/src/vendored/r3_hex_licenses.erl @@ -1,4 +1,4 @@ -%% Vendored from hex_core v0.15.0, do not edit manually +%% Vendored from hex_core v0.17.0, do not edit manually %% @doc %% Hex Licenses. @@ -667,4 +667,30 @@ valid(<<"xlock">>) -> true; valid(<<"xpp">>) -> true; valid(<<"xzoom">>) -> true; valid(<<"zlib-acknowledgement">>) -> true; +valid(<<"LicenseRef-", IdString/binary>>) -> valid_license_ref_idstring(IdString); valid(_) -> false. + +valid_license_ref_idstring(<<>>) -> + false; +valid_license_ref_idstring(IdString) -> + valid_license_ref_idstring(IdString, true). + +valid_license_ref_idstring(<<>>, Valid) -> + Valid; +valid_license_ref_idstring(_, false) -> + false; +valid_license_ref_idstring(<>, true) -> + valid_license_ref_idstring(Rest, valid_license_ref_char(Char)). + +valid_license_ref_char(Char) when Char >= $A, Char =< $Z -> + true; +valid_license_ref_char(Char) when Char >= $a, Char =< $z -> + true; +valid_license_ref_char(Char) when Char >= $0, Char =< $9 -> + true; +valid_license_ref_char($-) -> + true; +valid_license_ref_char($.) -> + true; +valid_license_ref_char(_) -> + false. diff --git a/apps/rebar/src/vendored/r3_hex_pb_names.erl b/apps/rebar/src/vendored/r3_hex_pb_names.erl index 8c2c940fe..473580102 100644 --- a/apps/rebar/src/vendored/r3_hex_pb_names.erl +++ b/apps/rebar/src/vendored/r3_hex_pb_names.erl @@ -1,4 +1,4 @@ -%% Vendored from hex_core v0.15.0, do not edit manually +%% Vendored from hex_core v0.17.0, do not edit manually %% -*- coding: utf-8 -*- %% % this file is @generated diff --git a/apps/rebar/src/vendored/r3_hex_pb_package.erl b/apps/rebar/src/vendored/r3_hex_pb_package.erl index 656f32ec8..d2ecfed99 100644 --- a/apps/rebar/src/vendored/r3_hex_pb_package.erl +++ b/apps/rebar/src/vendored/r3_hex_pb_package.erl @@ -1,4 +1,4 @@ -%% Vendored from hex_core v0.15.0, do not edit manually +%% Vendored from hex_core v0.17.0, do not edit manually %% -*- coding: utf-8 -*- %% % this file is @generated @@ -21,6 +21,7 @@ -export([find_enum_def/1, fetch_enum_def/1]). -export([enum_symbol_by_value/2, enum_value_by_symbol/2]). -export([enum_symbol_by_value_RetirementReason/1, enum_value_by_symbol_RetirementReason/1]). +-export([enum_symbol_by_value_AdvisorySeverity/1, enum_value_by_symbol_AdvisorySeverity/1]). -export([get_service_names/0]). -export([get_service_def/1]). -export([get_rpc_names/1]). @@ -53,13 +54,15 @@ %% enumerated types -type 'RetirementReason'() :: 'RETIRED_OTHER' | 'RETIRED_INVALID' | 'RETIRED_SECURITY' | 'RETIRED_DEPRECATED' | 'RETIRED_RENAMED'. --export_type(['RetirementReason'/0]). +-type 'AdvisorySeverity'() :: 'SEVERITY_NONE' | 'SEVERITY_LOW' | 'SEVERITY_MEDIUM' | 'SEVERITY_HIGH' | 'SEVERITY_CRITICAL'. +-export_type(['RetirementReason'/0, 'AdvisorySeverity'/0]). %% message types -type 'Package'() :: #{releases => ['Release'()], % = 1, repeated name => unicode:chardata(), % = 2, required - repository => unicode:chardata() % = 3, required + repository => unicode:chardata(), % = 3, required + advisories => ['SecurityAdvisory'()] % = 4, repeated }. -type 'Release'() :: @@ -67,7 +70,9 @@ inner_checksum => iodata(), % = 2, required dependencies => ['Dependency'()], % = 3, repeated retired => 'RetirementStatus'(), % = 4, optional - outer_checksum => iodata() % = 5, optional + outer_checksum => iodata(), % = 5, optional + advisory_indexes => [non_neg_integer()], % = 6, repeated, 32 bits + published_at => 'Timestamp'() % = 7, optional }. -type 'RetirementStatus'() :: @@ -75,6 +80,16 @@ message => unicode:chardata() % = 2, optional }. +-type 'SecurityAdvisory'() :: + #{id => unicode:chardata(), % = 1, required + summary => unicode:chardata(), % = 2, required + html_url => unicode:chardata(), % = 3, required + severity => 'SEVERITY_NONE' | 'SEVERITY_LOW' | 'SEVERITY_MEDIUM' | 'SEVERITY_HIGH' | 'SEVERITY_CRITICAL' | integer(), % = 4, optional, enum AdvisorySeverity + cvss_score => float() | integer() | infinity | '-infinity' | nan, % = 5, optional + api_url => unicode:chardata(), % = 6, required + aliases => [unicode:chardata()] % = 7, repeated + }. + -type 'Dependency'() :: #{package => unicode:chardata(), % = 1, required requirement => unicode:chardata(), % = 2, required @@ -83,9 +98,14 @@ repository => unicode:chardata() % = 5, optional }. --export_type(['Package'/0, 'Release'/0, 'RetirementStatus'/0, 'Dependency'/0]). --type '$msg_name'() :: 'Package' | 'Release' | 'RetirementStatus' | 'Dependency'. --type '$msg'() :: 'Package'() | 'Release'() | 'RetirementStatus'() | 'Dependency'(). +-type 'Timestamp'() :: + #{seconds => integer(), % = 1, required, 64 bits + nanos => integer() % = 2, required, 32 bits + }. + +-export_type(['Package'/0, 'Release'/0, 'RetirementStatus'/0, 'SecurityAdvisory'/0, 'Dependency'/0, 'Timestamp'/0]). +-type '$msg_name'() :: 'Package' | 'Release' | 'RetirementStatus' | 'SecurityAdvisory' | 'Dependency' | 'Timestamp'. +-type '$msg'() :: 'Package'() | 'Release'() | 'RetirementStatus'() | 'SecurityAdvisory'() | 'Dependency'() | 'Timestamp'(). -export_type(['$msg_name'/0, '$msg'/0]). -if(?OTP_RELEASE >= 24). @@ -105,7 +125,9 @@ encode_msg(Msg, MsgName, Opts) -> 'Package' -> encode_msg_Package(id(Msg, TrUserData), TrUserData); 'Release' -> encode_msg_Release(id(Msg, TrUserData), TrUserData); 'RetirementStatus' -> encode_msg_RetirementStatus(id(Msg, TrUserData), TrUserData); - 'Dependency' -> encode_msg_Dependency(id(Msg, TrUserData), TrUserData) + 'SecurityAdvisory' -> encode_msg_SecurityAdvisory(id(Msg, TrUserData), TrUserData); + 'Dependency' -> encode_msg_Dependency(id(Msg, TrUserData), TrUserData); + 'Timestamp' -> encode_msg_Timestamp(id(Msg, TrUserData), TrUserData) end. @@ -122,7 +144,15 @@ encode_msg_Package(#{name := F2, repository := F3} = M, Bin, TrUserData) -> _ -> Bin end, B2 = begin TrF2 = id(F2, TrUserData), e_type_string(TrF2, <>, TrUserData) end, - begin TrF3 = id(F3, TrUserData), e_type_string(TrF3, <>, TrUserData) end. + B3 = begin TrF3 = id(F3, TrUserData), e_type_string(TrF3, <>, TrUserData) end, + case M of + #{advisories := F4} -> + TrF4 = id(F4, TrUserData), + if TrF4 == [] -> B3; + true -> e_field_Package_advisories(TrF4, B3, TrUserData) + end; + _ -> B3 + end. encode_msg_Release(Msg, TrUserData) -> encode_msg_Release(Msg, <<>>, TrUserData). @@ -142,9 +172,21 @@ encode_msg_Release(#{version := F1, inner_checksum := F2} = M, Bin, TrUserData) #{retired := F4} -> begin TrF4 = id(F4, TrUserData), e_mfield_Release_retired(TrF4, <>, TrUserData) end; _ -> B3 end, + B5 = case M of + #{outer_checksum := F5} -> begin TrF5 = id(F5, TrUserData), e_type_bytes(TrF5, <>, TrUserData) end; + _ -> B4 + end, + B6 = case M of + #{advisory_indexes := F6} -> + TrF6 = id(F6, TrUserData), + if TrF6 == [] -> B5; + true -> e_field_Release_advisory_indexes(TrF6, B5, TrUserData) + end; + _ -> B5 + end, case M of - #{outer_checksum := F5} -> begin TrF5 = id(F5, TrUserData), e_type_bytes(TrF5, <>, TrUserData) end; - _ -> B4 + #{published_at := F7} -> begin TrF7 = id(F7, TrUserData), e_mfield_Release_published_at(TrF7, <>, TrUserData) end; + _ -> B6 end. encode_msg_RetirementStatus(Msg, TrUserData) -> encode_msg_RetirementStatus(Msg, <<>>, TrUserData). @@ -157,6 +199,31 @@ encode_msg_RetirementStatus(#{reason := F1} = M, Bin, TrUserData) -> _ -> B1 end. +encode_msg_SecurityAdvisory(Msg, TrUserData) -> encode_msg_SecurityAdvisory(Msg, <<>>, TrUserData). + + +encode_msg_SecurityAdvisory(#{id := F1, summary := F2, html_url := F3, api_url := F6} = M, Bin, TrUserData) -> + B1 = begin TrF1 = id(F1, TrUserData), e_type_string(TrF1, <>, TrUserData) end, + B2 = begin TrF2 = id(F2, TrUserData), e_type_string(TrF2, <>, TrUserData) end, + B3 = begin TrF3 = id(F3, TrUserData), e_type_string(TrF3, <>, TrUserData) end, + B4 = case M of + #{severity := F4} -> begin TrF4 = id(F4, TrUserData), e_enum_AdvisorySeverity(TrF4, <>, TrUserData) end; + _ -> B3 + end, + B5 = case M of + #{cvss_score := F5} -> begin TrF5 = id(F5, TrUserData), e_type_float(TrF5, <>, TrUserData) end; + _ -> B4 + end, + B6 = begin TrF6 = id(F6, TrUserData), e_type_string(TrF6, <>, TrUserData) end, + case M of + #{aliases := F7} -> + TrF7 = id(F7, TrUserData), + if TrF7 == [] -> B6; + true -> e_field_SecurityAdvisory_aliases(TrF7, B6, TrUserData) + end; + _ -> B6 + end. + encode_msg_Dependency(Msg, TrUserData) -> encode_msg_Dependency(Msg, <<>>, TrUserData). @@ -176,6 +243,13 @@ encode_msg_Dependency(#{package := F1, requirement := F2} = M, Bin, TrUserData) _ -> B4 end. +encode_msg_Timestamp(Msg, TrUserData) -> encode_msg_Timestamp(Msg, <<>>, TrUserData). + + +encode_msg_Timestamp(#{seconds := F1, nanos := F2}, Bin, TrUserData) -> + B1 = begin TrF1 = id(F1, TrUserData), e_type_int64(TrF1, <>, TrUserData) end, + begin TrF2 = id(F2, TrUserData), e_type_int32(TrF2, <>, TrUserData) end. + e_mfield_Package_releases(Msg, Bin, TrUserData) -> SubBin = encode_msg_Release(Msg, <<>>, TrUserData), Bin2 = e_varint(byte_size(SubBin), Bin), @@ -187,6 +261,17 @@ e_field_Package_releases([Elem | Rest], Bin, TrUserData) -> e_field_Package_releases(Rest, Bin3, TrUserData); e_field_Package_releases([], Bin, _TrUserData) -> Bin. +e_mfield_Package_advisories(Msg, Bin, TrUserData) -> + SubBin = encode_msg_SecurityAdvisory(Msg, <<>>, TrUserData), + Bin2 = e_varint(byte_size(SubBin), Bin), + <>. + +e_field_Package_advisories([Elem | Rest], Bin, TrUserData) -> + Bin2 = <>, + Bin3 = e_mfield_Package_advisories(id(Elem, TrUserData), Bin2, TrUserData), + e_field_Package_advisories(Rest, Bin3, TrUserData); +e_field_Package_advisories([], Bin, _TrUserData) -> Bin. + e_mfield_Release_dependencies(Msg, Bin, TrUserData) -> SubBin = encode_msg_Dependency(Msg, <<>>, TrUserData), Bin2 = e_varint(byte_size(SubBin), Bin), @@ -203,6 +288,23 @@ e_mfield_Release_retired(Msg, Bin, TrUserData) -> Bin2 = e_varint(byte_size(SubBin), Bin), <>. +e_field_Release_advisory_indexes([Elem | Rest], Bin, TrUserData) -> + Bin2 = <>, + Bin3 = e_varint(id(Elem, TrUserData), Bin2, TrUserData), + e_field_Release_advisory_indexes(Rest, Bin3, TrUserData); +e_field_Release_advisory_indexes([], Bin, _TrUserData) -> Bin. + +e_mfield_Release_published_at(Msg, Bin, TrUserData) -> + SubBin = encode_msg_Timestamp(Msg, <<>>, TrUserData), + Bin2 = e_varint(byte_size(SubBin), Bin), + <>. + +e_field_SecurityAdvisory_aliases([Elem | Rest], Bin, TrUserData) -> + Bin2 = <>, + Bin3 = e_type_string(id(Elem, TrUserData), Bin2, TrUserData), + e_field_SecurityAdvisory_aliases(Rest, Bin3, TrUserData); +e_field_SecurityAdvisory_aliases([], Bin, _TrUserData) -> Bin. + e_enum_RetirementReason('RETIRED_OTHER', Bin, _TrUserData) -> <>; e_enum_RetirementReason('RETIRED_INVALID', Bin, _TrUserData) -> <>; e_enum_RetirementReason('RETIRED_SECURITY', Bin, _TrUserData) -> <>; @@ -210,6 +312,13 @@ e_enum_RetirementReason('RETIRED_DEPRECATED', Bin, _TrUserData) -> < <>; e_enum_RetirementReason(V, Bin, _TrUserData) -> e_varint(V, Bin). +e_enum_AdvisorySeverity('SEVERITY_NONE', Bin, _TrUserData) -> <>; +e_enum_AdvisorySeverity('SEVERITY_LOW', Bin, _TrUserData) -> <>; +e_enum_AdvisorySeverity('SEVERITY_MEDIUM', Bin, _TrUserData) -> <>; +e_enum_AdvisorySeverity('SEVERITY_HIGH', Bin, _TrUserData) -> <>; +e_enum_AdvisorySeverity('SEVERITY_CRITICAL', Bin, _TrUserData) -> <>; +e_enum_AdvisorySeverity(V, Bin, _TrUserData) -> e_varint(V, Bin). + -compile({nowarn_unused_function,e_type_sint/3}). e_type_sint(Value, Bin, _TrUserData) when Value >= 0 -> e_varint(Value * 2, Bin); e_type_sint(Value, Bin, _TrUserData) -> e_varint(Value * -2 - 1, Bin). @@ -335,143 +444,172 @@ decode_msg_1_catch(Bin, MsgName, TrUserData) -> decode_msg_2_doit('Package', Bin, TrUserData) -> id(decode_msg_Package(Bin, TrUserData), TrUserData); decode_msg_2_doit('Release', Bin, TrUserData) -> id(decode_msg_Release(Bin, TrUserData), TrUserData); decode_msg_2_doit('RetirementStatus', Bin, TrUserData) -> id(decode_msg_RetirementStatus(Bin, TrUserData), TrUserData); -decode_msg_2_doit('Dependency', Bin, TrUserData) -> id(decode_msg_Dependency(Bin, TrUserData), TrUserData). +decode_msg_2_doit('SecurityAdvisory', Bin, TrUserData) -> id(decode_msg_SecurityAdvisory(Bin, TrUserData), TrUserData); +decode_msg_2_doit('Dependency', Bin, TrUserData) -> id(decode_msg_Dependency(Bin, TrUserData), TrUserData); +decode_msg_2_doit('Timestamp', Bin, TrUserData) -> id(decode_msg_Timestamp(Bin, TrUserData), TrUserData). -decode_msg_Package(Bin, TrUserData) -> dfp_read_field_def_Package(Bin, 0, 0, 0, id([], TrUserData), id('$undef', TrUserData), id('$undef', TrUserData), TrUserData). +decode_msg_Package(Bin, TrUserData) -> dfp_read_field_def_Package(Bin, 0, 0, 0, id([], TrUserData), id('$undef', TrUserData), id('$undef', TrUserData), id([], TrUserData), TrUserData). -dfp_read_field_def_Package(<<10, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> d_field_Package_releases(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData); -dfp_read_field_def_Package(<<18, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> d_field_Package_name(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData); -dfp_read_field_def_Package(<<26, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> d_field_Package_repository(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData); -dfp_read_field_def_Package(<<>>, 0, 0, _, R1, F@_2, F@_3, TrUserData) -> +dfp_read_field_def_Package(<<10, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> d_field_Package_releases(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData); +dfp_read_field_def_Package(<<18, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> d_field_Package_name(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData); +dfp_read_field_def_Package(<<26, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> d_field_Package_repository(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData); +dfp_read_field_def_Package(<<34, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> d_field_Package_advisories(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData); +dfp_read_field_def_Package(<<>>, 0, 0, _, R1, F@_2, F@_3, R2, TrUserData) -> S1 = #{name => F@_2, repository => F@_3}, - if R1 == '$undef' -> S1; - true -> S1#{releases => lists_reverse(R1, TrUserData)} + S2 = if R1 == '$undef' -> S1; + true -> S1#{releases => lists_reverse(R1, TrUserData)} + end, + if R2 == '$undef' -> S2; + true -> S2#{advisories => lists_reverse(R2, TrUserData)} end; -dfp_read_field_def_Package(Other, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> dg_read_field_def_Package(Other, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData). +dfp_read_field_def_Package(Other, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> dg_read_field_def_Package(Other, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData). -dg_read_field_def_Package(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 32 - 7 -> dg_read_field_def_Package(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); -dg_read_field_def_Package(<<0:1, X:7, Rest/binary>>, N, Acc, _, F@_1, F@_2, F@_3, TrUserData) -> +dg_read_field_def_Package(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 32 - 7 -> dg_read_field_def_Package(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); +dg_read_field_def_Package(<<0:1, X:7, Rest/binary>>, N, Acc, _, F@_1, F@_2, F@_3, F@_4, TrUserData) -> Key = X bsl N + Acc, case Key of - 10 -> d_field_Package_releases(Rest, 0, 0, 0, F@_1, F@_2, F@_3, TrUserData); - 18 -> d_field_Package_name(Rest, 0, 0, 0, F@_1, F@_2, F@_3, TrUserData); - 26 -> d_field_Package_repository(Rest, 0, 0, 0, F@_1, F@_2, F@_3, TrUserData); + 10 -> d_field_Package_releases(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, TrUserData); + 18 -> d_field_Package_name(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, TrUserData); + 26 -> d_field_Package_repository(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, TrUserData); + 34 -> d_field_Package_advisories(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, TrUserData); _ -> case Key band 7 of - 0 -> skip_varint_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, TrUserData); - 1 -> skip_64_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, TrUserData); - 2 -> skip_length_delimited_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, TrUserData); - 3 -> skip_group_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, TrUserData); - 5 -> skip_32_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, TrUserData) + 0 -> skip_varint_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, TrUserData); + 1 -> skip_64_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, TrUserData); + 2 -> skip_length_delimited_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, TrUserData); + 3 -> skip_group_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, TrUserData); + 5 -> skip_32_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, TrUserData) end end; -dg_read_field_def_Package(<<>>, 0, 0, _, R1, F@_2, F@_3, TrUserData) -> +dg_read_field_def_Package(<<>>, 0, 0, _, R1, F@_2, F@_3, R2, TrUserData) -> S1 = #{name => F@_2, repository => F@_3}, - if R1 == '$undef' -> S1; - true -> S1#{releases => lists_reverse(R1, TrUserData)} + S2 = if R1 == '$undef' -> S1; + true -> S1#{releases => lists_reverse(R1, TrUserData)} + end, + if R2 == '$undef' -> S2; + true -> S2#{advisories => lists_reverse(R2, TrUserData)} end. -d_field_Package_releases(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 57 -> d_field_Package_releases(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); -d_field_Package_releases(<<0:1, X:7, Rest/binary>>, N, Acc, F, Prev, F@_2, F@_3, TrUserData) -> +d_field_Package_releases(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 57 -> d_field_Package_releases(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); +d_field_Package_releases(<<0:1, X:7, Rest/binary>>, N, Acc, F, Prev, F@_2, F@_3, F@_4, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, {id(decode_msg_Release(Bs, TrUserData), TrUserData), Rest2} end, - dfp_read_field_def_Package(RestF, 0, 0, F, cons(NewFValue, Prev, TrUserData), F@_2, F@_3, TrUserData). + dfp_read_field_def_Package(RestF, 0, 0, F, cons(NewFValue, Prev, TrUserData), F@_2, F@_3, F@_4, TrUserData). -d_field_Package_name(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 57 -> d_field_Package_name(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); -d_field_Package_name(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, _, F@_3, TrUserData) -> +d_field_Package_name(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 57 -> d_field_Package_name(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); +d_field_Package_name(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, _, F@_3, F@_4, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, - dfp_read_field_def_Package(RestF, 0, 0, F, F@_1, NewFValue, F@_3, TrUserData). + dfp_read_field_def_Package(RestF, 0, 0, F, F@_1, NewFValue, F@_3, F@_4, TrUserData). -d_field_Package_repository(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 57 -> d_field_Package_repository(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); -d_field_Package_repository(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, _, TrUserData) -> +d_field_Package_repository(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 57 -> d_field_Package_repository(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); +d_field_Package_repository(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, _, F@_4, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, - dfp_read_field_def_Package(RestF, 0, 0, F, F@_1, F@_2, NewFValue, TrUserData). + dfp_read_field_def_Package(RestF, 0, 0, F, F@_1, F@_2, NewFValue, F@_4, TrUserData). + +d_field_Package_advisories(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 57 -> d_field_Package_advisories(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); +d_field_Package_advisories(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, Prev, TrUserData) -> + {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, {id(decode_msg_SecurityAdvisory(Bs, TrUserData), TrUserData), Rest2} end, + dfp_read_field_def_Package(RestF, 0, 0, F, F@_1, F@_2, F@_3, cons(NewFValue, Prev, TrUserData), TrUserData). -skip_varint_Package(<<1:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> skip_varint_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData); -skip_varint_Package(<<0:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> dfp_read_field_def_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData). +skip_varint_Package(<<1:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> skip_varint_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData); +skip_varint_Package(<<0:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> dfp_read_field_def_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData). -skip_length_delimited_Package(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 57 -> skip_length_delimited_Package(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); -skip_length_delimited_Package(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) -> +skip_length_delimited_Package(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 57 -> skip_length_delimited_Package(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); +skip_length_delimited_Package(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> Length = X bsl N + Acc, <<_:Length/binary, Rest2/binary>> = Rest, - dfp_read_field_def_Package(Rest2, 0, 0, F, F@_1, F@_2, F@_3, TrUserData). + dfp_read_field_def_Package(Rest2, 0, 0, F, F@_1, F@_2, F@_3, F@_4, TrUserData). -skip_group_Package(Bin, _, Z2, FNum, F@_1, F@_2, F@_3, TrUserData) -> +skip_group_Package(Bin, _, Z2, FNum, F@_1, F@_2, F@_3, F@_4, TrUserData) -> {_, Rest} = read_group(Bin, FNum), - dfp_read_field_def_Package(Rest, 0, Z2, FNum, F@_1, F@_2, F@_3, TrUserData). + dfp_read_field_def_Package(Rest, 0, Z2, FNum, F@_1, F@_2, F@_3, F@_4, TrUserData). -skip_32_Package(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> dfp_read_field_def_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData). +skip_32_Package(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> dfp_read_field_def_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData). -skip_64_Package(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> dfp_read_field_def_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData). +skip_64_Package(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> dfp_read_field_def_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData). -decode_msg_Release(Bin, TrUserData) -> dfp_read_field_def_Release(Bin, 0, 0, 0, id('$undef', TrUserData), id('$undef', TrUserData), id([], TrUserData), id('$undef', TrUserData), id('$undef', TrUserData), TrUserData). +decode_msg_Release(Bin, TrUserData) -> + dfp_read_field_def_Release(Bin, 0, 0, 0, id('$undef', TrUserData), id('$undef', TrUserData), id([], TrUserData), id('$undef', TrUserData), id('$undef', TrUserData), id([], TrUserData), id('$undef', TrUserData), TrUserData). -dfp_read_field_def_Release(<<10, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> d_field_Release_version(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); -dfp_read_field_def_Release(<<18, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> d_field_Release_inner_checksum(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); -dfp_read_field_def_Release(<<26, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> d_field_Release_dependencies(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); -dfp_read_field_def_Release(<<34, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> d_field_Release_retired(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); -dfp_read_field_def_Release(<<42, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> d_field_Release_outer_checksum(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); -dfp_read_field_def_Release(<<>>, 0, 0, _, F@_1, F@_2, R1, F@_4, F@_5, TrUserData) -> - S1 = #{version => F@_1, inner_checksum => F@_2}, +dfp_read_field_def_Release(<<10, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_Release_version(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +dfp_read_field_def_Release(<<18, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_Release_inner_checksum(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +dfp_read_field_def_Release(<<26, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_Release_dependencies(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +dfp_read_field_def_Release(<<34, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_Release_retired(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +dfp_read_field_def_Release(<<42, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_Release_outer_checksum(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +dfp_read_field_def_Release(<<50, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_pfield_Release_advisory_indexes(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +dfp_read_field_def_Release(<<48, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_Release_advisory_indexes(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +dfp_read_field_def_Release(<<58, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_Release_published_at(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +dfp_read_field_def_Release(<<>>, 0, 0, _, F@_1, F@_2, R1, F@_4, F@_5, R2, F@_7, TrUserData) -> + S1 = #{version => F@_1, inner_checksum => F@_2, advisory_indexes => lists_reverse(R2, TrUserData)}, S2 = if R1 == '$undef' -> S1; true -> S1#{dependencies => lists_reverse(R1, TrUserData)} end, S3 = if F@_4 == '$undef' -> S2; true -> S2#{retired => F@_4} end, - if F@_5 == '$undef' -> S3; - true -> S3#{outer_checksum => F@_5} + S4 = if F@_5 == '$undef' -> S3; + true -> S3#{outer_checksum => F@_5} + end, + if F@_7 == '$undef' -> S4; + true -> S4#{published_at => F@_7} end; -dfp_read_field_def_Release(Other, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> dg_read_field_def_Release(Other, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData). +dfp_read_field_def_Release(Other, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> dg_read_field_def_Release(Other, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). -dg_read_field_def_Release(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) when N < 32 - 7 -> dg_read_field_def_Release(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); -dg_read_field_def_Release(<<0:1, X:7, Rest/binary>>, N, Acc, _, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> +dg_read_field_def_Release(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 32 - 7 -> dg_read_field_def_Release(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +dg_read_field_def_Release(<<0:1, X:7, Rest/binary>>, N, Acc, _, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> Key = X bsl N + Acc, case Key of - 10 -> d_field_Release_version(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); - 18 -> d_field_Release_inner_checksum(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); - 26 -> d_field_Release_dependencies(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); - 34 -> d_field_Release_retired(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); - 42 -> d_field_Release_outer_checksum(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); + 10 -> d_field_Release_version(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); + 18 -> d_field_Release_inner_checksum(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); + 26 -> d_field_Release_dependencies(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); + 34 -> d_field_Release_retired(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); + 42 -> d_field_Release_outer_checksum(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); + 50 -> d_pfield_Release_advisory_indexes(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); + 48 -> d_field_Release_advisory_indexes(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); + 58 -> d_field_Release_published_at(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); _ -> case Key band 7 of - 0 -> skip_varint_Release(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); - 1 -> skip_64_Release(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); - 2 -> skip_length_delimited_Release(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); - 3 -> skip_group_Release(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); - 5 -> skip_32_Release(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) + 0 -> skip_varint_Release(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); + 1 -> skip_64_Release(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); + 2 -> skip_length_delimited_Release(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); + 3 -> skip_group_Release(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); + 5 -> skip_32_Release(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) end end; -dg_read_field_def_Release(<<>>, 0, 0, _, F@_1, F@_2, R1, F@_4, F@_5, TrUserData) -> - S1 = #{version => F@_1, inner_checksum => F@_2}, +dg_read_field_def_Release(<<>>, 0, 0, _, F@_1, F@_2, R1, F@_4, F@_5, R2, F@_7, TrUserData) -> + S1 = #{version => F@_1, inner_checksum => F@_2, advisory_indexes => lists_reverse(R2, TrUserData)}, S2 = if R1 == '$undef' -> S1; true -> S1#{dependencies => lists_reverse(R1, TrUserData)} end, S3 = if F@_4 == '$undef' -> S2; true -> S2#{retired => F@_4} end, - if F@_5 == '$undef' -> S3; - true -> S3#{outer_checksum => F@_5} + S4 = if F@_5 == '$undef' -> S3; + true -> S3#{outer_checksum => F@_5} + end, + if F@_7 == '$undef' -> S4; + true -> S4#{published_at => F@_7} end. -d_field_Release_version(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) when N < 57 -> d_field_Release_version(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); -d_field_Release_version(<<0:1, X:7, Rest/binary>>, N, Acc, F, _, F@_2, F@_3, F@_4, F@_5, TrUserData) -> +d_field_Release_version(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> d_field_Release_version(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +d_field_Release_version(<<0:1, X:7, Rest/binary>>, N, Acc, F, _, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, - dfp_read_field_def_Release(RestF, 0, 0, F, NewFValue, F@_2, F@_3, F@_4, F@_5, TrUserData). + dfp_read_field_def_Release(RestF, 0, 0, F, NewFValue, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). -d_field_Release_inner_checksum(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) when N < 57 -> d_field_Release_inner_checksum(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); -d_field_Release_inner_checksum(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, _, F@_3, F@_4, F@_5, TrUserData) -> +d_field_Release_inner_checksum(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> + d_field_Release_inner_checksum(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +d_field_Release_inner_checksum(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, _, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, - dfp_read_field_def_Release(RestF, 0, 0, F, F@_1, NewFValue, F@_3, F@_4, F@_5, TrUserData). + dfp_read_field_def_Release(RestF, 0, 0, F, F@_1, NewFValue, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). -d_field_Release_dependencies(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) when N < 57 -> d_field_Release_dependencies(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); -d_field_Release_dependencies(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, Prev, F@_4, F@_5, TrUserData) -> +d_field_Release_dependencies(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> d_field_Release_dependencies(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +d_field_Release_dependencies(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, Prev, F@_4, F@_5, F@_6, F@_7, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, {id(decode_msg_Dependency(Bs, TrUserData), TrUserData), Rest2} end, - dfp_read_field_def_Release(RestF, 0, 0, F, F@_1, F@_2, cons(NewFValue, Prev, TrUserData), F@_4, F@_5, TrUserData). + dfp_read_field_def_Release(RestF, 0, 0, F, F@_1, F@_2, cons(NewFValue, Prev, TrUserData), F@_4, F@_5, F@_6, F@_7, TrUserData). -d_field_Release_retired(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) when N < 57 -> d_field_Release_retired(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); -d_field_Release_retired(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, Prev, F@_5, TrUserData) -> +d_field_Release_retired(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> d_field_Release_retired(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +d_field_Release_retired(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, Prev, F@_5, F@_6, F@_7, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, {id(decode_msg_RetirementStatus(Bs, TrUserData), TrUserData), Rest2} end, dfp_read_field_def_Release(RestF, 0, @@ -484,29 +622,71 @@ d_field_Release_retired(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, true -> merge_msg_RetirementStatus(Prev, NewFValue, TrUserData) end, F@_5, + F@_6, + F@_7, TrUserData). -d_field_Release_outer_checksum(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) when N < 57 -> d_field_Release_outer_checksum(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); -d_field_Release_outer_checksum(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, _, TrUserData) -> +d_field_Release_outer_checksum(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> + d_field_Release_outer_checksum(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +d_field_Release_outer_checksum(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, _, F@_6, F@_7, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, - dfp_read_field_def_Release(RestF, 0, 0, F, F@_1, F@_2, F@_3, F@_4, NewFValue, TrUserData). + dfp_read_field_def_Release(RestF, 0, 0, F, F@_1, F@_2, F@_3, F@_4, NewFValue, F@_6, F@_7, TrUserData). -skip_varint_Release(<<1:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> skip_varint_Release(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); -skip_varint_Release(<<0:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> dfp_read_field_def_Release(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData). +d_field_Release_advisory_indexes(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> + d_field_Release_advisory_indexes(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +d_field_Release_advisory_indexes(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, Prev, F@_7, TrUserData) -> + {NewFValue, RestF} = {id((X bsl N + Acc) band 4294967295, TrUserData), Rest}, + dfp_read_field_def_Release(RestF, 0, 0, F, F@_1, F@_2, F@_3, F@_4, F@_5, cons(NewFValue, Prev, TrUserData), F@_7, TrUserData). -skip_length_delimited_Release(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) when N < 57 -> skip_length_delimited_Release(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); -skip_length_delimited_Release(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> +d_pfield_Release_advisory_indexes(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> + d_pfield_Release_advisory_indexes(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +d_pfield_Release_advisory_indexes(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, E, F@_7, TrUserData) -> + Len = X bsl N + Acc, + <> = Rest, + NewSeq = d_packed_field_Release_advisory_indexes(PackedBytes, 0, 0, F, E, TrUserData), + dfp_read_field_def_Release(Rest2, 0, 0, F, F@_1, F@_2, F@_3, F@_4, F@_5, NewSeq, F@_7, TrUserData). + +d_packed_field_Release_advisory_indexes(<<1:1, X:7, Rest/binary>>, N, Acc, F, AccSeq, TrUserData) when N < 57 -> d_packed_field_Release_advisory_indexes(Rest, N + 7, X bsl N + Acc, F, AccSeq, TrUserData); +d_packed_field_Release_advisory_indexes(<<0:1, X:7, Rest/binary>>, N, Acc, F, AccSeq, TrUserData) -> + {NewFValue, RestF} = {id((X bsl N + Acc) band 4294967295, TrUserData), Rest}, + d_packed_field_Release_advisory_indexes(RestF, 0, 0, F, [NewFValue | AccSeq], TrUserData); +d_packed_field_Release_advisory_indexes(<<>>, 0, 0, _, AccSeq, _) -> AccSeq. + +d_field_Release_published_at(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> d_field_Release_published_at(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +d_field_Release_published_at(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, Prev, TrUserData) -> + {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, {id(decode_msg_Timestamp(Bs, TrUserData), TrUserData), Rest2} end, + dfp_read_field_def_Release(RestF, + 0, + 0, + F, + F@_1, + F@_2, + F@_3, + F@_4, + F@_5, + F@_6, + if Prev == '$undef' -> NewFValue; + true -> merge_msg_Timestamp(Prev, NewFValue, TrUserData) + end, + TrUserData). + +skip_varint_Release(<<1:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> skip_varint_Release(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +skip_varint_Release(<<0:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> dfp_read_field_def_Release(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). + +skip_length_delimited_Release(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> + skip_length_delimited_Release(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +skip_length_delimited_Release(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> Length = X bsl N + Acc, <<_:Length/binary, Rest2/binary>> = Rest, - dfp_read_field_def_Release(Rest2, 0, 0, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData). + dfp_read_field_def_Release(Rest2, 0, 0, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). -skip_group_Release(Bin, _, Z2, FNum, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> +skip_group_Release(Bin, _, Z2, FNum, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> {_, Rest} = read_group(Bin, FNum), - dfp_read_field_def_Release(Rest, 0, Z2, FNum, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData). + dfp_read_field_def_Release(Rest, 0, Z2, FNum, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). -skip_32_Release(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> dfp_read_field_def_Release(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData). +skip_32_Release(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> dfp_read_field_def_Release(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). -skip_64_Release(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> dfp_read_field_def_Release(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData). +skip_64_Release(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> dfp_read_field_def_Release(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). decode_msg_RetirementStatus(Bin, TrUserData) -> dfp_read_field_def_RetirementStatus(Bin, 0, 0, 0, id('$undef', TrUserData), id('$undef', TrUserData), TrUserData). @@ -567,6 +747,118 @@ skip_32_RetirementStatus(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserDat skip_64_RetirementStatus(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dfp_read_field_def_RetirementStatus(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData). +decode_msg_SecurityAdvisory(Bin, TrUserData) -> + dfp_read_field_def_SecurityAdvisory(Bin, 0, 0, 0, id('$undef', TrUserData), id('$undef', TrUserData), id('$undef', TrUserData), id('$undef', TrUserData), id('$undef', TrUserData), id('$undef', TrUserData), id([], TrUserData), TrUserData). + +dfp_read_field_def_SecurityAdvisory(<<10, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_SecurityAdvisory_id(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +dfp_read_field_def_SecurityAdvisory(<<18, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_SecurityAdvisory_summary(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +dfp_read_field_def_SecurityAdvisory(<<26, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_SecurityAdvisory_html_url(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +dfp_read_field_def_SecurityAdvisory(<<32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_SecurityAdvisory_severity(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +dfp_read_field_def_SecurityAdvisory(<<45, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_SecurityAdvisory_cvss_score(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +dfp_read_field_def_SecurityAdvisory(<<50, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_SecurityAdvisory_api_url(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +dfp_read_field_def_SecurityAdvisory(<<58, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_SecurityAdvisory_aliases(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +dfp_read_field_def_SecurityAdvisory(<<>>, 0, 0, _, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, R1, TrUserData) -> + S1 = #{id => F@_1, summary => F@_2, html_url => F@_3, api_url => F@_6, aliases => lists_reverse(R1, TrUserData)}, + S2 = if F@_4 == '$undef' -> S1; + true -> S1#{severity => F@_4} + end, + if F@_5 == '$undef' -> S2; + true -> S2#{cvss_score => F@_5} + end; +dfp_read_field_def_SecurityAdvisory(Other, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> dg_read_field_def_SecurityAdvisory(Other, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). + +dg_read_field_def_SecurityAdvisory(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 32 - 7 -> + dg_read_field_def_SecurityAdvisory(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +dg_read_field_def_SecurityAdvisory(<<0:1, X:7, Rest/binary>>, N, Acc, _, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> + Key = X bsl N + Acc, + case Key of + 10 -> d_field_SecurityAdvisory_id(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); + 18 -> d_field_SecurityAdvisory_summary(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); + 26 -> d_field_SecurityAdvisory_html_url(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); + 32 -> d_field_SecurityAdvisory_severity(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); + 45 -> d_field_SecurityAdvisory_cvss_score(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); + 50 -> d_field_SecurityAdvisory_api_url(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); + 58 -> d_field_SecurityAdvisory_aliases(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); + _ -> + case Key band 7 of + 0 -> skip_varint_SecurityAdvisory(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); + 1 -> skip_64_SecurityAdvisory(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); + 2 -> skip_length_delimited_SecurityAdvisory(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); + 3 -> skip_group_SecurityAdvisory(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); + 5 -> skip_32_SecurityAdvisory(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) + end + end; +dg_read_field_def_SecurityAdvisory(<<>>, 0, 0, _, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, R1, TrUserData) -> + S1 = #{id => F@_1, summary => F@_2, html_url => F@_3, api_url => F@_6, aliases => lists_reverse(R1, TrUserData)}, + S2 = if F@_4 == '$undef' -> S1; + true -> S1#{severity => F@_4} + end, + if F@_5 == '$undef' -> S2; + true -> S2#{cvss_score => F@_5} + end. + +d_field_SecurityAdvisory_id(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> d_field_SecurityAdvisory_id(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +d_field_SecurityAdvisory_id(<<0:1, X:7, Rest/binary>>, N, Acc, F, _, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> + {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, + dfp_read_field_def_SecurityAdvisory(RestF, 0, 0, F, NewFValue, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). + +d_field_SecurityAdvisory_summary(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> + d_field_SecurityAdvisory_summary(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +d_field_SecurityAdvisory_summary(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, _, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> + {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, + dfp_read_field_def_SecurityAdvisory(RestF, 0, 0, F, F@_1, NewFValue, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). + +d_field_SecurityAdvisory_html_url(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> + d_field_SecurityAdvisory_html_url(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +d_field_SecurityAdvisory_html_url(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, _, F@_4, F@_5, F@_6, F@_7, TrUserData) -> + {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, + dfp_read_field_def_SecurityAdvisory(RestF, 0, 0, F, F@_1, F@_2, NewFValue, F@_4, F@_5, F@_6, F@_7, TrUserData). + +d_field_SecurityAdvisory_severity(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> + d_field_SecurityAdvisory_severity(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +d_field_SecurityAdvisory_severity(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, _, F@_5, F@_6, F@_7, TrUserData) -> + {NewFValue, RestF} = {id(d_enum_AdvisorySeverity(begin <> = <<(X bsl N + Acc):32/unsigned-native>>, id(Res, TrUserData) end), TrUserData), Rest}, + dfp_read_field_def_SecurityAdvisory(RestF, 0, 0, F, F@_1, F@_2, F@_3, NewFValue, F@_5, F@_6, F@_7, TrUserData). + +d_field_SecurityAdvisory_cvss_score(<<0:16, 128, 127, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, _, F@_6, F@_7, TrUserData) -> + dfp_read_field_def_SecurityAdvisory(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, id(infinity, TrUserData), F@_6, F@_7, TrUserData); +d_field_SecurityAdvisory_cvss_score(<<0:16, 128, 255, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, _, F@_6, F@_7, TrUserData) -> + dfp_read_field_def_SecurityAdvisory(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, id('-infinity', TrUserData), F@_6, F@_7, TrUserData); +d_field_SecurityAdvisory_cvss_score(<<_:16, 1:1, _:7, _:1, 127:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, _, F@_6, F@_7, TrUserData) -> + dfp_read_field_def_SecurityAdvisory(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, id(nan, TrUserData), F@_6, F@_7, TrUserData); +d_field_SecurityAdvisory_cvss_score(<>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, _, F@_6, F@_7, TrUserData) -> + dfp_read_field_def_SecurityAdvisory(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, id(Value, TrUserData), F@_6, F@_7, TrUserData). + +d_field_SecurityAdvisory_api_url(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> + d_field_SecurityAdvisory_api_url(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +d_field_SecurityAdvisory_api_url(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, _, F@_7, TrUserData) -> + {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, + dfp_read_field_def_SecurityAdvisory(RestF, 0, 0, F, F@_1, F@_2, F@_3, F@_4, F@_5, NewFValue, F@_7, TrUserData). + +d_field_SecurityAdvisory_aliases(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> + d_field_SecurityAdvisory_aliases(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +d_field_SecurityAdvisory_aliases(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, Prev, TrUserData) -> + {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, + dfp_read_field_def_SecurityAdvisory(RestF, 0, 0, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, cons(NewFValue, Prev, TrUserData), TrUserData). + +skip_varint_SecurityAdvisory(<<1:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> skip_varint_SecurityAdvisory(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +skip_varint_SecurityAdvisory(<<0:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> dfp_read_field_def_SecurityAdvisory(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). + +skip_length_delimited_SecurityAdvisory(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> + skip_length_delimited_SecurityAdvisory(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); +skip_length_delimited_SecurityAdvisory(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> + Length = X bsl N + Acc, + <<_:Length/binary, Rest2/binary>> = Rest, + dfp_read_field_def_SecurityAdvisory(Rest2, 0, 0, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). + +skip_group_SecurityAdvisory(Bin, _, Z2, FNum, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> + {_, Rest} = read_group(Bin, FNum), + dfp_read_field_def_SecurityAdvisory(Rest, 0, Z2, FNum, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). + +skip_32_SecurityAdvisory(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> dfp_read_field_def_SecurityAdvisory(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). + +skip_64_SecurityAdvisory(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> dfp_read_field_def_SecurityAdvisory(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). + decode_msg_Dependency(Bin, TrUserData) -> dfp_read_field_def_Dependency(Bin, 0, 0, 0, id('$undef', TrUserData), id('$undef', TrUserData), id('$undef', TrUserData), id('$undef', TrUserData), id('$undef', TrUserData), TrUserData). dfp_read_field_def_Dependency(<<10, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> d_field_Dependency_package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); @@ -659,6 +951,57 @@ skip_32_Dependency(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_ skip_64_Dependency(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> dfp_read_field_def_Dependency(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData). +decode_msg_Timestamp(Bin, TrUserData) -> dfp_read_field_def_Timestamp(Bin, 0, 0, 0, id('$undef', TrUserData), id('$undef', TrUserData), TrUserData). + +dfp_read_field_def_Timestamp(<<8, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> d_field_Timestamp_seconds(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData); +dfp_read_field_def_Timestamp(<<16, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> d_field_Timestamp_nanos(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData); +dfp_read_field_def_Timestamp(<<>>, 0, 0, _, F@_1, F@_2, _) -> #{seconds => F@_1, nanos => F@_2}; +dfp_read_field_def_Timestamp(Other, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dg_read_field_def_Timestamp(Other, Z1, Z2, F, F@_1, F@_2, TrUserData). + +dg_read_field_def_Timestamp(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 32 - 7 -> dg_read_field_def_Timestamp(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); +dg_read_field_def_Timestamp(<<0:1, X:7, Rest/binary>>, N, Acc, _, F@_1, F@_2, TrUserData) -> + Key = X bsl N + Acc, + case Key of + 8 -> d_field_Timestamp_seconds(Rest, 0, 0, 0, F@_1, F@_2, TrUserData); + 16 -> d_field_Timestamp_nanos(Rest, 0, 0, 0, F@_1, F@_2, TrUserData); + _ -> + case Key band 7 of + 0 -> skip_varint_Timestamp(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); + 1 -> skip_64_Timestamp(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); + 2 -> skip_length_delimited_Timestamp(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); + 3 -> skip_group_Timestamp(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); + 5 -> skip_32_Timestamp(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData) + end + end; +dg_read_field_def_Timestamp(<<>>, 0, 0, _, F@_1, F@_2, _) -> #{seconds => F@_1, nanos => F@_2}. + +d_field_Timestamp_seconds(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 57 -> d_field_Timestamp_seconds(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); +d_field_Timestamp_seconds(<<0:1, X:7, Rest/binary>>, N, Acc, F, _, F@_2, TrUserData) -> + {NewFValue, RestF} = {begin <> = <<(X bsl N + Acc):64/unsigned-native>>, id(Res, TrUserData) end, Rest}, + dfp_read_field_def_Timestamp(RestF, 0, 0, F, NewFValue, F@_2, TrUserData). + +d_field_Timestamp_nanos(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 57 -> d_field_Timestamp_nanos(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); +d_field_Timestamp_nanos(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, _, TrUserData) -> + {NewFValue, RestF} = {begin <> = <<(X bsl N + Acc):32/unsigned-native>>, id(Res, TrUserData) end, Rest}, + dfp_read_field_def_Timestamp(RestF, 0, 0, F, F@_1, NewFValue, TrUserData). + +skip_varint_Timestamp(<<1:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> skip_varint_Timestamp(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData); +skip_varint_Timestamp(<<0:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dfp_read_field_def_Timestamp(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData). + +skip_length_delimited_Timestamp(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 57 -> skip_length_delimited_Timestamp(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); +skip_length_delimited_Timestamp(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) -> + Length = X bsl N + Acc, + <<_:Length/binary, Rest2/binary>> = Rest, + dfp_read_field_def_Timestamp(Rest2, 0, 0, F, F@_1, F@_2, TrUserData). + +skip_group_Timestamp(Bin, _, Z2, FNum, F@_1, F@_2, TrUserData) -> + {_, Rest} = read_group(Bin, FNum), + dfp_read_field_def_Timestamp(Rest, 0, Z2, FNum, F@_1, F@_2, TrUserData). + +skip_32_Timestamp(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dfp_read_field_def_Timestamp(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData). + +skip_64_Timestamp(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dfp_read_field_def_Timestamp(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData). + d_enum_RetirementReason(0) -> 'RETIRED_OTHER'; d_enum_RetirementReason(1) -> 'RETIRED_INVALID'; d_enum_RetirementReason(2) -> 'RETIRED_SECURITY'; @@ -666,6 +1009,13 @@ d_enum_RetirementReason(3) -> 'RETIRED_DEPRECATED'; d_enum_RetirementReason(4) -> 'RETIRED_RENAMED'; d_enum_RetirementReason(V) -> V. +d_enum_AdvisorySeverity(0) -> 'SEVERITY_NONE'; +d_enum_AdvisorySeverity(1) -> 'SEVERITY_LOW'; +d_enum_AdvisorySeverity(2) -> 'SEVERITY_MEDIUM'; +d_enum_AdvisorySeverity(3) -> 'SEVERITY_HIGH'; +d_enum_AdvisorySeverity(4) -> 'SEVERITY_CRITICAL'; +d_enum_AdvisorySeverity(V) -> V. + read_group(Bin, FieldNum) -> {NumBytes, EndTagLen} = read_gr_b(Bin, 0, 0, 0, 0, FieldNum), <> = Bin, @@ -732,17 +1082,25 @@ merge_msgs(Prev, New, MsgName, Opts) -> 'Package' -> merge_msg_Package(Prev, New, TrUserData); 'Release' -> merge_msg_Release(Prev, New, TrUserData); 'RetirementStatus' -> merge_msg_RetirementStatus(Prev, New, TrUserData); - 'Dependency' -> merge_msg_Dependency(Prev, New, TrUserData) + 'SecurityAdvisory' -> merge_msg_SecurityAdvisory(Prev, New, TrUserData); + 'Dependency' -> merge_msg_Dependency(Prev, New, TrUserData); + 'Timestamp' -> merge_msg_Timestamp(Prev, New, TrUserData) end. -compile({nowarn_unused_function,merge_msg_Package/3}). merge_msg_Package(#{} = PMsg, #{name := NFname, repository := NFrepository} = NMsg, TrUserData) -> S1 = #{name => NFname, repository => NFrepository}, + S2 = case {PMsg, NMsg} of + {#{releases := PFreleases}, #{releases := NFreleases}} -> S1#{releases => 'erlang_++'(PFreleases, NFreleases, TrUserData)}; + {_, #{releases := NFreleases}} -> S1#{releases => NFreleases}; + {#{releases := PFreleases}, _} -> S1#{releases => PFreleases}; + {_, _} -> S1 + end, case {PMsg, NMsg} of - {#{releases := PFreleases}, #{releases := NFreleases}} -> S1#{releases => 'erlang_++'(PFreleases, NFreleases, TrUserData)}; - {_, #{releases := NFreleases}} -> S1#{releases => NFreleases}; - {#{releases := PFreleases}, _} -> S1#{releases => PFreleases}; - {_, _} -> S1 + {#{advisories := PFadvisories}, #{advisories := NFadvisories}} -> S2#{advisories => 'erlang_++'(PFadvisories, NFadvisories, TrUserData)}; + {_, #{advisories := NFadvisories}} -> S2#{advisories => NFadvisories}; + {#{advisories := PFadvisories}, _} -> S2#{advisories => PFadvisories}; + {_, _} -> S2 end. -compile({nowarn_unused_function,merge_msg_Release/3}). @@ -760,10 +1118,22 @@ merge_msg_Release(#{} = PMsg, #{version := NFversion, inner_checksum := NFinner_ {#{retired := PFretired}, _} -> S2#{retired => PFretired}; {_, _} -> S2 end, + S4 = case {PMsg, NMsg} of + {_, #{outer_checksum := NFouter_checksum}} -> S3#{outer_checksum => NFouter_checksum}; + {#{outer_checksum := PFouter_checksum}, _} -> S3#{outer_checksum => PFouter_checksum}; + _ -> S3 + end, + S5 = case {PMsg, NMsg} of + {#{advisory_indexes := PFadvisory_indexes}, #{advisory_indexes := NFadvisory_indexes}} -> S4#{advisory_indexes => 'erlang_++'(PFadvisory_indexes, NFadvisory_indexes, TrUserData)}; + {_, #{advisory_indexes := NFadvisory_indexes}} -> S4#{advisory_indexes => NFadvisory_indexes}; + {#{advisory_indexes := PFadvisory_indexes}, _} -> S4#{advisory_indexes => PFadvisory_indexes}; + {_, _} -> S4 + end, case {PMsg, NMsg} of - {_, #{outer_checksum := NFouter_checksum}} -> S3#{outer_checksum => NFouter_checksum}; - {#{outer_checksum := PFouter_checksum}, _} -> S3#{outer_checksum => PFouter_checksum}; - _ -> S3 + {#{published_at := PFpublished_at}, #{published_at := NFpublished_at}} -> S5#{published_at => merge_msg_Timestamp(PFpublished_at, NFpublished_at, TrUserData)}; + {_, #{published_at := NFpublished_at}} -> S5#{published_at => NFpublished_at}; + {#{published_at := PFpublished_at}, _} -> S5#{published_at => PFpublished_at}; + {_, _} -> S5 end. -compile({nowarn_unused_function,merge_msg_RetirementStatus/3}). @@ -775,6 +1145,26 @@ merge_msg_RetirementStatus(#{} = PMsg, #{reason := NFreason} = NMsg, _) -> _ -> S1 end. +-compile({nowarn_unused_function,merge_msg_SecurityAdvisory/3}). +merge_msg_SecurityAdvisory(#{} = PMsg, #{id := NFid, summary := NFsummary, html_url := NFhtml_url, api_url := NFapi_url} = NMsg, TrUserData) -> + S1 = #{id => NFid, summary => NFsummary, html_url => NFhtml_url, api_url => NFapi_url}, + S2 = case {PMsg, NMsg} of + {_, #{severity := NFseverity}} -> S1#{severity => NFseverity}; + {#{severity := PFseverity}, _} -> S1#{severity => PFseverity}; + _ -> S1 + end, + S3 = case {PMsg, NMsg} of + {_, #{cvss_score := NFcvss_score}} -> S2#{cvss_score => NFcvss_score}; + {#{cvss_score := PFcvss_score}, _} -> S2#{cvss_score => PFcvss_score}; + _ -> S2 + end, + case {PMsg, NMsg} of + {#{aliases := PFaliases}, #{aliases := NFaliases}} -> S3#{aliases => 'erlang_++'(PFaliases, NFaliases, TrUserData)}; + {_, #{aliases := NFaliases}} -> S3#{aliases => NFaliases}; + {#{aliases := PFaliases}, _} -> S3#{aliases => PFaliases}; + {_, _} -> S3 + end. + -compile({nowarn_unused_function,merge_msg_Dependency/3}). merge_msg_Dependency(#{} = PMsg, #{package := NFpackage, requirement := NFrequirement} = NMsg, _) -> S1 = #{package => NFpackage, requirement => NFrequirement}, @@ -794,6 +1184,9 @@ merge_msg_Dependency(#{} = PMsg, #{package := NFpackage, requirement := NFrequir _ -> S3 end. +-compile({nowarn_unused_function,merge_msg_Timestamp/3}). +merge_msg_Timestamp(#{}, #{seconds := NFseconds, nanos := NFnanos}, _) -> #{seconds => NFseconds, nanos => NFnanos}. + verify_msg(Msg, MsgName) when is_atom(MsgName) -> verify_msg(Msg, MsgName, []). @@ -803,7 +1196,9 @@ verify_msg(Msg, MsgName, Opts) -> 'Package' -> v_msg_Package(Msg, [MsgName], TrUserData); 'Release' -> v_msg_Release(Msg, [MsgName], TrUserData); 'RetirementStatus' -> v_msg_RetirementStatus(Msg, [MsgName], TrUserData); + 'SecurityAdvisory' -> v_msg_SecurityAdvisory(Msg, [MsgName], TrUserData); 'Dependency' -> v_msg_Dependency(Msg, [MsgName], TrUserData); + 'Timestamp' -> v_msg_Timestamp(Msg, [MsgName], TrUserData); _ -> mk_type_error(not_a_known_message, Msg, []) end. @@ -822,9 +1217,19 @@ v_msg_Package(#{name := F2, repository := F3} = M, Path, TrUserData) -> end, v_type_string(F2, [name | Path], TrUserData), v_type_string(F3, [repository | Path], TrUserData), + case M of + #{advisories := F4} -> + if is_list(F4) -> + _ = [v_submsg_SecurityAdvisory(Elem, [advisories | Path], TrUserData) || Elem <- F4], + ok; + true -> mk_type_error({invalid_list_of, {msg, 'SecurityAdvisory'}}, F4, [advisories | Path]) + end; + _ -> ok + end, lists:foreach(fun (releases) -> ok; (name) -> ok; (repository) -> ok; + (advisories) -> ok; (OtherKey) -> mk_type_error({extraneous_key, OtherKey}, M, Path) end, maps:keys(M)), @@ -858,11 +1263,26 @@ v_msg_Release(#{version := F1, inner_checksum := F2} = M, Path, TrUserData) -> #{outer_checksum := F5} -> v_type_bytes(F5, [outer_checksum | Path], TrUserData); _ -> ok end, + case M of + #{advisory_indexes := F6} -> + if is_list(F6) -> + _ = [v_type_uint32(Elem, [advisory_indexes | Path], TrUserData) || Elem <- F6], + ok; + true -> mk_type_error({invalid_list_of, uint32}, F6, [advisory_indexes | Path]) + end; + _ -> ok + end, + case M of + #{published_at := F7} -> v_submsg_Timestamp(F7, [published_at | Path], TrUserData); + _ -> ok + end, lists:foreach(fun (version) -> ok; (inner_checksum) -> ok; (dependencies) -> ok; (retired) -> ok; (outer_checksum) -> ok; + (advisory_indexes) -> ok; + (published_at) -> ok; (OtherKey) -> mk_type_error({extraneous_key, OtherKey}, M, Path) end, maps:keys(M)), @@ -891,6 +1311,48 @@ v_msg_RetirementStatus(#{reason := F1} = M, Path, TrUserData) -> v_msg_RetirementStatus(M, Path, _TrUserData) when is_map(M) -> mk_type_error({missing_fields, [reason] -- maps:keys(M), 'RetirementStatus'}, M, Path); v_msg_RetirementStatus(X, Path, _TrUserData) -> mk_type_error({expected_msg, 'RetirementStatus'}, X, Path). +-compile({nowarn_unused_function,v_submsg_SecurityAdvisory/3}). +-dialyzer({nowarn_function,v_submsg_SecurityAdvisory/3}). +v_submsg_SecurityAdvisory(Msg, Path, TrUserData) -> v_msg_SecurityAdvisory(Msg, Path, TrUserData). + +-compile({nowarn_unused_function,v_msg_SecurityAdvisory/3}). +-dialyzer({nowarn_function,v_msg_SecurityAdvisory/3}). +v_msg_SecurityAdvisory(#{id := F1, summary := F2, html_url := F3, api_url := F6} = M, Path, TrUserData) -> + v_type_string(F1, [id | Path], TrUserData), + v_type_string(F2, [summary | Path], TrUserData), + v_type_string(F3, [html_url | Path], TrUserData), + case M of + #{severity := F4} -> v_enum_AdvisorySeverity(F4, [severity | Path], TrUserData); + _ -> ok + end, + case M of + #{cvss_score := F5} -> v_type_float(F5, [cvss_score | Path], TrUserData); + _ -> ok + end, + v_type_string(F6, [api_url | Path], TrUserData), + case M of + #{aliases := F7} -> + if is_list(F7) -> + _ = [v_type_string(Elem, [aliases | Path], TrUserData) || Elem <- F7], + ok; + true -> mk_type_error({invalid_list_of, string}, F7, [aliases | Path]) + end; + _ -> ok + end, + lists:foreach(fun (id) -> ok; + (summary) -> ok; + (html_url) -> ok; + (severity) -> ok; + (cvss_score) -> ok; + (api_url) -> ok; + (aliases) -> ok; + (OtherKey) -> mk_type_error({extraneous_key, OtherKey}, M, Path) + end, + maps:keys(M)), + ok; +v_msg_SecurityAdvisory(M, Path, _TrUserData) when is_map(M) -> mk_type_error({missing_fields, [id, summary, html_url, api_url] -- maps:keys(M), 'SecurityAdvisory'}, M, Path); +v_msg_SecurityAdvisory(X, Path, _TrUserData) -> mk_type_error({expected_msg, 'SecurityAdvisory'}, X, Path). + -compile({nowarn_unused_function,v_submsg_Dependency/3}). -dialyzer({nowarn_function,v_submsg_Dependency/3}). v_submsg_Dependency(Msg, Path, TrUserData) -> v_msg_Dependency(Msg, Path, TrUserData). @@ -924,6 +1386,24 @@ v_msg_Dependency(#{package := F1, requirement := F2} = M, Path, TrUserData) -> v_msg_Dependency(M, Path, _TrUserData) when is_map(M) -> mk_type_error({missing_fields, [package, requirement] -- maps:keys(M), 'Dependency'}, M, Path); v_msg_Dependency(X, Path, _TrUserData) -> mk_type_error({expected_msg, 'Dependency'}, X, Path). +-compile({nowarn_unused_function,v_submsg_Timestamp/3}). +-dialyzer({nowarn_function,v_submsg_Timestamp/3}). +v_submsg_Timestamp(Msg, Path, TrUserData) -> v_msg_Timestamp(Msg, Path, TrUserData). + +-compile({nowarn_unused_function,v_msg_Timestamp/3}). +-dialyzer({nowarn_function,v_msg_Timestamp/3}). +v_msg_Timestamp(#{seconds := F1, nanos := F2} = M, Path, TrUserData) -> + v_type_int64(F1, [seconds | Path], TrUserData), + v_type_int32(F2, [nanos | Path], TrUserData), + lists:foreach(fun (seconds) -> ok; + (nanos) -> ok; + (OtherKey) -> mk_type_error({extraneous_key, OtherKey}, M, Path) + end, + maps:keys(M)), + ok; +v_msg_Timestamp(M, Path, _TrUserData) when is_map(M) -> mk_type_error({missing_fields, [seconds, nanos] -- maps:keys(M), 'Timestamp'}, M, Path); +v_msg_Timestamp(X, Path, _TrUserData) -> mk_type_error({expected_msg, 'Timestamp'}, X, Path). + -compile({nowarn_unused_function,v_enum_RetirementReason/3}). -dialyzer({nowarn_function,v_enum_RetirementReason/3}). v_enum_RetirementReason('RETIRED_OTHER', _Path, _TrUserData) -> ok; @@ -934,6 +1414,34 @@ v_enum_RetirementReason('RETIRED_RENAMED', _Path, _TrUserData) -> ok; v_enum_RetirementReason(V, _Path, _TrUserData) when -2147483648 =< V, V =< 2147483647, is_integer(V) -> ok; v_enum_RetirementReason(X, Path, _TrUserData) -> mk_type_error({invalid_enum, 'RetirementReason'}, X, Path). +-compile({nowarn_unused_function,v_enum_AdvisorySeverity/3}). +-dialyzer({nowarn_function,v_enum_AdvisorySeverity/3}). +v_enum_AdvisorySeverity('SEVERITY_NONE', _Path, _TrUserData) -> ok; +v_enum_AdvisorySeverity('SEVERITY_LOW', _Path, _TrUserData) -> ok; +v_enum_AdvisorySeverity('SEVERITY_MEDIUM', _Path, _TrUserData) -> ok; +v_enum_AdvisorySeverity('SEVERITY_HIGH', _Path, _TrUserData) -> ok; +v_enum_AdvisorySeverity('SEVERITY_CRITICAL', _Path, _TrUserData) -> ok; +v_enum_AdvisorySeverity(V, _Path, _TrUserData) when -2147483648 =< V, V =< 2147483647, is_integer(V) -> ok; +v_enum_AdvisorySeverity(X, Path, _TrUserData) -> mk_type_error({invalid_enum, 'AdvisorySeverity'}, X, Path). + +-compile({nowarn_unused_function,v_type_int32/3}). +-dialyzer({nowarn_function,v_type_int32/3}). +v_type_int32(N, _Path, _TrUserData) when is_integer(N), -2147483648 =< N, N =< 2147483647 -> ok; +v_type_int32(N, Path, _TrUserData) when is_integer(N) -> mk_type_error({value_out_of_range, int32, signed, 32}, N, Path); +v_type_int32(X, Path, _TrUserData) -> mk_type_error({bad_integer, int32, signed, 32}, X, Path). + +-compile({nowarn_unused_function,v_type_int64/3}). +-dialyzer({nowarn_function,v_type_int64/3}). +v_type_int64(N, _Path, _TrUserData) when is_integer(N), -9223372036854775808 =< N, N =< 9223372036854775807 -> ok; +v_type_int64(N, Path, _TrUserData) when is_integer(N) -> mk_type_error({value_out_of_range, int64, signed, 64}, N, Path); +v_type_int64(X, Path, _TrUserData) -> mk_type_error({bad_integer, int64, signed, 64}, X, Path). + +-compile({nowarn_unused_function,v_type_uint32/3}). +-dialyzer({nowarn_function,v_type_uint32/3}). +v_type_uint32(N, _Path, _TrUserData) when is_integer(N), 0 =< N, N =< 4294967295 -> ok; +v_type_uint32(N, Path, _TrUserData) when is_integer(N) -> mk_type_error({value_out_of_range, uint32, unsigned, 32}, N, Path); +v_type_uint32(X, Path, _TrUserData) -> mk_type_error({bad_integer, uint32, unsigned, 32}, X, Path). + -compile({nowarn_unused_function,v_type_bool/3}). -dialyzer({nowarn_function,v_type_bool/3}). v_type_bool(false, _Path, _TrUserData) -> ok; @@ -942,6 +1450,15 @@ v_type_bool(0, _Path, _TrUserData) -> ok; v_type_bool(1, _Path, _TrUserData) -> ok; v_type_bool(X, Path, _TrUserData) -> mk_type_error(bad_boolean_value, X, Path). +-compile({nowarn_unused_function,v_type_float/3}). +-dialyzer({nowarn_function,v_type_float/3}). +v_type_float(N, _Path, _TrUserData) when is_float(N) -> ok; +v_type_float(N, _Path, _TrUserData) when is_integer(N) -> ok; +v_type_float(infinity, _Path, _TrUserData) -> ok; +v_type_float('-infinity', _Path, _TrUserData) -> ok; +v_type_float(nan, _Path, _TrUserData) -> ok; +v_type_float(X, Path, _TrUserData) -> mk_type_error(bad_float_value, X, Path). + -compile({nowarn_unused_function,v_type_string/3}). -dialyzer({nowarn_function,v_type_string/3}). v_type_string(S, Path, _TrUserData) when is_list(S); is_binary(S) -> @@ -998,35 +1515,48 @@ cons(Elem, Acc, _TrUserData) -> [Elem | Acc]. get_msg_defs() -> [{{enum, 'RetirementReason'}, [{'RETIRED_OTHER', 0}, {'RETIRED_INVALID', 1}, {'RETIRED_SECURITY', 2}, {'RETIRED_DEPRECATED', 3}, {'RETIRED_RENAMED', 4}]}, + {{enum, 'AdvisorySeverity'}, [{'SEVERITY_NONE', 0}, {'SEVERITY_LOW', 1}, {'SEVERITY_MEDIUM', 2}, {'SEVERITY_HIGH', 3}, {'SEVERITY_CRITICAL', 4}]}, {{msg, 'Package'}, [#{name => releases, fnum => 1, rnum => 2, type => {msg, 'Release'}, occurrence => repeated, opts => []}, #{name => name, fnum => 2, rnum => 3, type => string, occurrence => required, opts => []}, - #{name => repository, fnum => 3, rnum => 4, type => string, occurrence => required, opts => []}]}, + #{name => repository, fnum => 3, rnum => 4, type => string, occurrence => required, opts => []}, + #{name => advisories, fnum => 4, rnum => 5, type => {msg, 'SecurityAdvisory'}, occurrence => repeated, opts => []}]}, {{msg, 'Release'}, [#{name => version, fnum => 1, rnum => 2, type => string, occurrence => required, opts => []}, #{name => inner_checksum, fnum => 2, rnum => 3, type => bytes, occurrence => required, opts => []}, #{name => dependencies, fnum => 3, rnum => 4, type => {msg, 'Dependency'}, occurrence => repeated, opts => []}, #{name => retired, fnum => 4, rnum => 5, type => {msg, 'RetirementStatus'}, occurrence => optional, opts => []}, - #{name => outer_checksum, fnum => 5, rnum => 6, type => bytes, occurrence => optional, opts => []}]}, + #{name => outer_checksum, fnum => 5, rnum => 6, type => bytes, occurrence => optional, opts => []}, + #{name => advisory_indexes, fnum => 6, rnum => 7, type => uint32, occurrence => repeated, opts => []}, + #{name => published_at, fnum => 7, rnum => 8, type => {msg, 'Timestamp'}, occurrence => optional, opts => []}]}, {{msg, 'RetirementStatus'}, [#{name => reason, fnum => 1, rnum => 2, type => {enum, 'RetirementReason'}, occurrence => required, opts => []}, #{name => message, fnum => 2, rnum => 3, type => string, occurrence => optional, opts => []}]}, + {{msg, 'SecurityAdvisory'}, + [#{name => id, fnum => 1, rnum => 2, type => string, occurrence => required, opts => []}, + #{name => summary, fnum => 2, rnum => 3, type => string, occurrence => required, opts => []}, + #{name => html_url, fnum => 3, rnum => 4, type => string, occurrence => required, opts => []}, + #{name => severity, fnum => 4, rnum => 5, type => {enum, 'AdvisorySeverity'}, occurrence => optional, opts => []}, + #{name => cvss_score, fnum => 5, rnum => 6, type => float, occurrence => optional, opts => []}, + #{name => api_url, fnum => 6, rnum => 7, type => string, occurrence => required, opts => []}, + #{name => aliases, fnum => 7, rnum => 8, type => string, occurrence => repeated, opts => []}]}, {{msg, 'Dependency'}, [#{name => package, fnum => 1, rnum => 2, type => string, occurrence => required, opts => []}, #{name => requirement, fnum => 2, rnum => 3, type => string, occurrence => required, opts => []}, #{name => optional, fnum => 3, rnum => 4, type => bool, occurrence => optional, opts => []}, #{name => app, fnum => 4, rnum => 5, type => string, occurrence => optional, opts => []}, - #{name => repository, fnum => 5, rnum => 6, type => string, occurrence => optional, opts => []}]}]. + #{name => repository, fnum => 5, rnum => 6, type => string, occurrence => optional, opts => []}]}, + {{msg, 'Timestamp'}, [#{name => seconds, fnum => 1, rnum => 2, type => int64, occurrence => required, opts => []}, #{name => nanos, fnum => 2, rnum => 3, type => int32, occurrence => required, opts => []}]}]. -get_msg_names() -> ['Package', 'Release', 'RetirementStatus', 'Dependency']. +get_msg_names() -> ['Package', 'Release', 'RetirementStatus', 'SecurityAdvisory', 'Dependency', 'Timestamp']. get_group_names() -> []. -get_msg_or_group_names() -> ['Package', 'Release', 'RetirementStatus', 'Dependency']. +get_msg_or_group_names() -> ['Package', 'Release', 'RetirementStatus', 'SecurityAdvisory', 'Dependency', 'Timestamp']. -get_enum_names() -> ['RetirementReason']. +get_enum_names() -> ['RetirementReason', 'AdvisorySeverity']. fetch_msg_def(MsgName) -> @@ -1046,31 +1576,46 @@ fetch_enum_def(EnumName) -> find_msg_def('Package') -> [#{name => releases, fnum => 1, rnum => 2, type => {msg, 'Release'}, occurrence => repeated, opts => []}, #{name => name, fnum => 2, rnum => 3, type => string, occurrence => required, opts => []}, - #{name => repository, fnum => 3, rnum => 4, type => string, occurrence => required, opts => []}]; + #{name => repository, fnum => 3, rnum => 4, type => string, occurrence => required, opts => []}, + #{name => advisories, fnum => 4, rnum => 5, type => {msg, 'SecurityAdvisory'}, occurrence => repeated, opts => []}]; find_msg_def('Release') -> [#{name => version, fnum => 1, rnum => 2, type => string, occurrence => required, opts => []}, #{name => inner_checksum, fnum => 2, rnum => 3, type => bytes, occurrence => required, opts => []}, #{name => dependencies, fnum => 3, rnum => 4, type => {msg, 'Dependency'}, occurrence => repeated, opts => []}, #{name => retired, fnum => 4, rnum => 5, type => {msg, 'RetirementStatus'}, occurrence => optional, opts => []}, - #{name => outer_checksum, fnum => 5, rnum => 6, type => bytes, occurrence => optional, opts => []}]; + #{name => outer_checksum, fnum => 5, rnum => 6, type => bytes, occurrence => optional, opts => []}, + #{name => advisory_indexes, fnum => 6, rnum => 7, type => uint32, occurrence => repeated, opts => []}, + #{name => published_at, fnum => 7, rnum => 8, type => {msg, 'Timestamp'}, occurrence => optional, opts => []}]; find_msg_def('RetirementStatus') -> [#{name => reason, fnum => 1, rnum => 2, type => {enum, 'RetirementReason'}, occurrence => required, opts => []}, #{name => message, fnum => 2, rnum => 3, type => string, occurrence => optional, opts => []}]; +find_msg_def('SecurityAdvisory') -> + [#{name => id, fnum => 1, rnum => 2, type => string, occurrence => required, opts => []}, + #{name => summary, fnum => 2, rnum => 3, type => string, occurrence => required, opts => []}, + #{name => html_url, fnum => 3, rnum => 4, type => string, occurrence => required, opts => []}, + #{name => severity, fnum => 4, rnum => 5, type => {enum, 'AdvisorySeverity'}, occurrence => optional, opts => []}, + #{name => cvss_score, fnum => 5, rnum => 6, type => float, occurrence => optional, opts => []}, + #{name => api_url, fnum => 6, rnum => 7, type => string, occurrence => required, opts => []}, + #{name => aliases, fnum => 7, rnum => 8, type => string, occurrence => repeated, opts => []}]; find_msg_def('Dependency') -> [#{name => package, fnum => 1, rnum => 2, type => string, occurrence => required, opts => []}, #{name => requirement, fnum => 2, rnum => 3, type => string, occurrence => required, opts => []}, #{name => optional, fnum => 3, rnum => 4, type => bool, occurrence => optional, opts => []}, #{name => app, fnum => 4, rnum => 5, type => string, occurrence => optional, opts => []}, #{name => repository, fnum => 5, rnum => 6, type => string, occurrence => optional, opts => []}]; +find_msg_def('Timestamp') -> [#{name => seconds, fnum => 1, rnum => 2, type => int64, occurrence => required, opts => []}, #{name => nanos, fnum => 2, rnum => 3, type => int32, occurrence => required, opts => []}]; find_msg_def(_) -> error. find_enum_def('RetirementReason') -> [{'RETIRED_OTHER', 0}, {'RETIRED_INVALID', 1}, {'RETIRED_SECURITY', 2}, {'RETIRED_DEPRECATED', 3}, {'RETIRED_RENAMED', 4}]; +find_enum_def('AdvisorySeverity') -> [{'SEVERITY_NONE', 0}, {'SEVERITY_LOW', 1}, {'SEVERITY_MEDIUM', 2}, {'SEVERITY_HIGH', 3}, {'SEVERITY_CRITICAL', 4}]; find_enum_def(_) -> error. -enum_symbol_by_value('RetirementReason', Value) -> enum_symbol_by_value_RetirementReason(Value). +enum_symbol_by_value('RetirementReason', Value) -> enum_symbol_by_value_RetirementReason(Value); +enum_symbol_by_value('AdvisorySeverity', Value) -> enum_symbol_by_value_AdvisorySeverity(Value). -enum_value_by_symbol('RetirementReason', Sym) -> enum_value_by_symbol_RetirementReason(Sym). +enum_value_by_symbol('RetirementReason', Sym) -> enum_value_by_symbol_RetirementReason(Sym); +enum_value_by_symbol('AdvisorySeverity', Sym) -> enum_value_by_symbol_AdvisorySeverity(Sym). enum_symbol_by_value_RetirementReason(0) -> 'RETIRED_OTHER'; @@ -1086,6 +1631,19 @@ enum_value_by_symbol_RetirementReason('RETIRED_SECURITY') -> 2; enum_value_by_symbol_RetirementReason('RETIRED_DEPRECATED') -> 3; enum_value_by_symbol_RetirementReason('RETIRED_RENAMED') -> 4. +enum_symbol_by_value_AdvisorySeverity(0) -> 'SEVERITY_NONE'; +enum_symbol_by_value_AdvisorySeverity(1) -> 'SEVERITY_LOW'; +enum_symbol_by_value_AdvisorySeverity(2) -> 'SEVERITY_MEDIUM'; +enum_symbol_by_value_AdvisorySeverity(3) -> 'SEVERITY_HIGH'; +enum_symbol_by_value_AdvisorySeverity(4) -> 'SEVERITY_CRITICAL'. + + +enum_value_by_symbol_AdvisorySeverity('SEVERITY_NONE') -> 0; +enum_value_by_symbol_AdvisorySeverity('SEVERITY_LOW') -> 1; +enum_value_by_symbol_AdvisorySeverity('SEVERITY_MEDIUM') -> 2; +enum_value_by_symbol_AdvisorySeverity('SEVERITY_HIGH') -> 3; +enum_value_by_symbol_AdvisorySeverity('SEVERITY_CRITICAL') -> 4. + get_service_names() -> []. @@ -1133,22 +1691,28 @@ service_and_rpc_name_to_fqbins(S, R) -> error({gpb_error, {badservice_or_rpc, {S fqbin_to_msg_name(<<"Package">>) -> 'Package'; fqbin_to_msg_name(<<"Release">>) -> 'Release'; fqbin_to_msg_name(<<"RetirementStatus">>) -> 'RetirementStatus'; +fqbin_to_msg_name(<<"SecurityAdvisory">>) -> 'SecurityAdvisory'; fqbin_to_msg_name(<<"Dependency">>) -> 'Dependency'; +fqbin_to_msg_name(<<"Timestamp">>) -> 'Timestamp'; fqbin_to_msg_name(E) -> error({gpb_error, {badmsg, E}}). msg_name_to_fqbin('Package') -> <<"Package">>; msg_name_to_fqbin('Release') -> <<"Release">>; msg_name_to_fqbin('RetirementStatus') -> <<"RetirementStatus">>; +msg_name_to_fqbin('SecurityAdvisory') -> <<"SecurityAdvisory">>; msg_name_to_fqbin('Dependency') -> <<"Dependency">>; +msg_name_to_fqbin('Timestamp') -> <<"Timestamp">>; msg_name_to_fqbin(E) -> error({gpb_error, {badmsg, E}}). fqbin_to_enum_name(<<"RetirementReason">>) -> 'RetirementReason'; +fqbin_to_enum_name(<<"AdvisorySeverity">>) -> 'AdvisorySeverity'; fqbin_to_enum_name(E) -> error({gpb_error, {badenum, E}}). enum_name_to_fqbin('RetirementReason') -> <<"RetirementReason">>; +enum_name_to_fqbin('AdvisorySeverity') -> <<"AdvisorySeverity">>; enum_name_to_fqbin(E) -> error({gpb_error, {badenum, E}}). @@ -1179,7 +1743,7 @@ get_all_source_basenames() -> ["r3_hex_pb_package.proto"]. get_all_proto_names() -> ["r3_hex_pb_package"]. -get_msg_containment("r3_hex_pb_package") -> ['Dependency', 'Package', 'Release', 'RetirementStatus']; +get_msg_containment("r3_hex_pb_package") -> ['Dependency', 'Package', 'Release', 'RetirementStatus', 'SecurityAdvisory', 'Timestamp']; get_msg_containment(P) -> error({gpb_error, {badproto, P}}). @@ -1195,13 +1759,15 @@ get_rpc_containment("r3_hex_pb_package") -> []; get_rpc_containment(P) -> error({gpb_error, {badproto, P}}). -get_enum_containment("r3_hex_pb_package") -> ['RetirementReason']; +get_enum_containment("r3_hex_pb_package") -> ['AdvisorySeverity', 'RetirementReason']; get_enum_containment(P) -> error({gpb_error, {badproto, P}}). +get_proto_by_msg_name_as_fqbin(<<"Timestamp">>) -> "r3_hex_pb_package"; get_proto_by_msg_name_as_fqbin(<<"RetirementStatus">>) -> "r3_hex_pb_package"; get_proto_by_msg_name_as_fqbin(<<"Release">>) -> "r3_hex_pb_package"; get_proto_by_msg_name_as_fqbin(<<"Package">>) -> "r3_hex_pb_package"; +get_proto_by_msg_name_as_fqbin(<<"SecurityAdvisory">>) -> "r3_hex_pb_package"; get_proto_by_msg_name_as_fqbin(<<"Dependency">>) -> "r3_hex_pb_package"; get_proto_by_msg_name_as_fqbin(E) -> error({gpb_error, {badmsg, E}}). @@ -1210,6 +1776,7 @@ get_proto_by_msg_name_as_fqbin(E) -> error({gpb_error, {badmsg, E}}). get_proto_by_service_name_as_fqbin(E) -> error({gpb_error, {badservice, E}}). +get_proto_by_enum_name_as_fqbin(<<"AdvisorySeverity">>) -> "r3_hex_pb_package"; get_proto_by_enum_name_as_fqbin(<<"RetirementReason">>) -> "r3_hex_pb_package"; get_proto_by_enum_name_as_fqbin(E) -> error({gpb_error, {badenum, E}}). diff --git a/apps/rebar/src/vendored/r3_hex_pb_signed.erl b/apps/rebar/src/vendored/r3_hex_pb_signed.erl index bfcfc7a94..b6835b737 100644 --- a/apps/rebar/src/vendored/r3_hex_pb_signed.erl +++ b/apps/rebar/src/vendored/r3_hex_pb_signed.erl @@ -1,4 +1,4 @@ -%% Vendored from hex_core v0.15.0, do not edit manually +%% Vendored from hex_core v0.17.0, do not edit manually %% -*- coding: utf-8 -*- %% % this file is @generated diff --git a/apps/rebar/src/vendored/r3_hex_pb_versions.erl b/apps/rebar/src/vendored/r3_hex_pb_versions.erl index b4af33e8f..2a6880887 100644 --- a/apps/rebar/src/vendored/r3_hex_pb_versions.erl +++ b/apps/rebar/src/vendored/r3_hex_pb_versions.erl @@ -1,4 +1,4 @@ -%% Vendored from hex_core v0.15.0, do not edit manually +%% Vendored from hex_core v0.17.0, do not edit manually %% -*- coding: utf-8 -*- %% % this file is @generated @@ -63,7 +63,8 @@ -type 'Package'() :: #{name => unicode:chardata(), % = 1, required versions => [unicode:chardata()], % = 2, repeated - retired => [integer()] % = 3, repeated, 32 bits + retired => [integer()], % = 3, repeated, 32 bits + with_advisories => [integer()] % = 5, repeated, 32 bits }. -export_type(['Versions'/0, 'Package'/0]). @@ -117,13 +118,21 @@ encode_msg_Package(#{name := F1} = M, Bin, TrUserData) -> end; _ -> B1 end, + B3 = case M of + #{retired := F3} -> + TrF3 = id(F3, TrUserData), + if TrF3 == [] -> B2; + true -> e_field_Package_retired(TrF3, B2, TrUserData) + end; + _ -> B2 + end, case M of - #{retired := F3} -> - TrF3 = id(F3, TrUserData), - if TrF3 == [] -> B2; - true -> e_field_Package_retired(TrF3, B2, TrUserData) + #{with_advisories := F4} -> + TrF4 = id(F4, TrUserData), + if TrF4 == [] -> B3; + true -> e_field_Package_with_advisories(TrF4, B3, TrUserData) end; - _ -> B2 + _ -> B3 end. e_mfield_Versions_packages(Msg, Bin, TrUserData) -> @@ -155,6 +164,18 @@ e_pfield_Package_retired([Value | Rest], Bin, TrUserData) -> e_pfield_Package_retired(Rest, Bin2, TrUserData); e_pfield_Package_retired([], Bin, _TrUserData) -> Bin. +e_field_Package_with_advisories(Elems, Bin, TrUserData) when Elems =/= [] -> + SubBin = e_pfield_Package_with_advisories(Elems, <<>>, TrUserData), + Bin2 = <>, + Bin3 = e_varint(byte_size(SubBin), Bin2), + <>; +e_field_Package_with_advisories([], Bin, _TrUserData) -> Bin. + +e_pfield_Package_with_advisories([Value | Rest], Bin, TrUserData) -> + Bin2 = e_type_int32(id(Value, TrUserData), Bin, TrUserData), + e_pfield_Package_with_advisories(Rest, Bin2, TrUserData); +e_pfield_Package_with_advisories([], Bin, _TrUserData) -> Bin. + -compile({nowarn_unused_function,e_type_sint/3}). e_type_sint(Value, Bin, _TrUserData) when Value >= 0 -> e_varint(Value * 2, Bin); e_type_sint(Value, Bin, _TrUserData) -> e_varint(Value * -2 - 1, Bin). @@ -341,55 +362,59 @@ skip_32_Versions(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> df skip_64_Versions(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dfp_read_field_def_Versions(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData). -decode_msg_Package(Bin, TrUserData) -> dfp_read_field_def_Package(Bin, 0, 0, 0, id('$undef', TrUserData), id([], TrUserData), id([], TrUserData), TrUserData). +decode_msg_Package(Bin, TrUserData) -> dfp_read_field_def_Package(Bin, 0, 0, 0, id('$undef', TrUserData), id([], TrUserData), id([], TrUserData), id([], TrUserData), TrUserData). -dfp_read_field_def_Package(<<10, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> d_field_Package_name(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData); -dfp_read_field_def_Package(<<18, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> d_field_Package_versions(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData); -dfp_read_field_def_Package(<<26, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> d_pfield_Package_retired(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData); -dfp_read_field_def_Package(<<24, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> d_field_Package_retired(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData); -dfp_read_field_def_Package(<<>>, 0, 0, _, F@_1, R1, R2, TrUserData) -> #{name => F@_1, versions => lists_reverse(R1, TrUserData), retired => lists_reverse(R2, TrUserData)}; -dfp_read_field_def_Package(Other, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> dg_read_field_def_Package(Other, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData). +dfp_read_field_def_Package(<<10, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> d_field_Package_name(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData); +dfp_read_field_def_Package(<<18, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> d_field_Package_versions(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData); +dfp_read_field_def_Package(<<26, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> d_pfield_Package_retired(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData); +dfp_read_field_def_Package(<<24, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> d_field_Package_retired(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData); +dfp_read_field_def_Package(<<42, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> d_pfield_Package_with_advisories(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData); +dfp_read_field_def_Package(<<40, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> d_field_Package_with_advisories(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData); +dfp_read_field_def_Package(<<>>, 0, 0, _, F@_1, R1, R2, R3, TrUserData) -> #{name => F@_1, versions => lists_reverse(R1, TrUserData), retired => lists_reverse(R2, TrUserData), with_advisories => lists_reverse(R3, TrUserData)}; +dfp_read_field_def_Package(Other, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> dg_read_field_def_Package(Other, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData). -dg_read_field_def_Package(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 32 - 7 -> dg_read_field_def_Package(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); -dg_read_field_def_Package(<<0:1, X:7, Rest/binary>>, N, Acc, _, F@_1, F@_2, F@_3, TrUserData) -> +dg_read_field_def_Package(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 32 - 7 -> dg_read_field_def_Package(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); +dg_read_field_def_Package(<<0:1, X:7, Rest/binary>>, N, Acc, _, F@_1, F@_2, F@_3, F@_4, TrUserData) -> Key = X bsl N + Acc, case Key of - 10 -> d_field_Package_name(Rest, 0, 0, 0, F@_1, F@_2, F@_3, TrUserData); - 18 -> d_field_Package_versions(Rest, 0, 0, 0, F@_1, F@_2, F@_3, TrUserData); - 26 -> d_pfield_Package_retired(Rest, 0, 0, 0, F@_1, F@_2, F@_3, TrUserData); - 24 -> d_field_Package_retired(Rest, 0, 0, 0, F@_1, F@_2, F@_3, TrUserData); + 10 -> d_field_Package_name(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, TrUserData); + 18 -> d_field_Package_versions(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, TrUserData); + 26 -> d_pfield_Package_retired(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, TrUserData); + 24 -> d_field_Package_retired(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, TrUserData); + 42 -> d_pfield_Package_with_advisories(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, TrUserData); + 40 -> d_field_Package_with_advisories(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, TrUserData); _ -> case Key band 7 of - 0 -> skip_varint_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, TrUserData); - 1 -> skip_64_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, TrUserData); - 2 -> skip_length_delimited_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, TrUserData); - 3 -> skip_group_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, TrUserData); - 5 -> skip_32_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, TrUserData) + 0 -> skip_varint_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, TrUserData); + 1 -> skip_64_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, TrUserData); + 2 -> skip_length_delimited_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, TrUserData); + 3 -> skip_group_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, TrUserData); + 5 -> skip_32_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, TrUserData) end end; -dg_read_field_def_Package(<<>>, 0, 0, _, F@_1, R1, R2, TrUserData) -> #{name => F@_1, versions => lists_reverse(R1, TrUserData), retired => lists_reverse(R2, TrUserData)}. +dg_read_field_def_Package(<<>>, 0, 0, _, F@_1, R1, R2, R3, TrUserData) -> #{name => F@_1, versions => lists_reverse(R1, TrUserData), retired => lists_reverse(R2, TrUserData), with_advisories => lists_reverse(R3, TrUserData)}. -d_field_Package_name(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 57 -> d_field_Package_name(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); -d_field_Package_name(<<0:1, X:7, Rest/binary>>, N, Acc, F, _, F@_2, F@_3, TrUserData) -> +d_field_Package_name(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 57 -> d_field_Package_name(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); +d_field_Package_name(<<0:1, X:7, Rest/binary>>, N, Acc, F, _, F@_2, F@_3, F@_4, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, - dfp_read_field_def_Package(RestF, 0, 0, F, NewFValue, F@_2, F@_3, TrUserData). + dfp_read_field_def_Package(RestF, 0, 0, F, NewFValue, F@_2, F@_3, F@_4, TrUserData). -d_field_Package_versions(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 57 -> d_field_Package_versions(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); -d_field_Package_versions(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, Prev, F@_3, TrUserData) -> +d_field_Package_versions(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 57 -> d_field_Package_versions(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); +d_field_Package_versions(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, Prev, F@_3, F@_4, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, - dfp_read_field_def_Package(RestF, 0, 0, F, F@_1, cons(NewFValue, Prev, TrUserData), F@_3, TrUserData). + dfp_read_field_def_Package(RestF, 0, 0, F, F@_1, cons(NewFValue, Prev, TrUserData), F@_3, F@_4, TrUserData). -d_field_Package_retired(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 57 -> d_field_Package_retired(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); -d_field_Package_retired(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, Prev, TrUserData) -> +d_field_Package_retired(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 57 -> d_field_Package_retired(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); +d_field_Package_retired(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, Prev, F@_4, TrUserData) -> {NewFValue, RestF} = {begin <> = <<(X bsl N + Acc):32/unsigned-native>>, id(Res, TrUserData) end, Rest}, - dfp_read_field_def_Package(RestF, 0, 0, F, F@_1, F@_2, cons(NewFValue, Prev, TrUserData), TrUserData). + dfp_read_field_def_Package(RestF, 0, 0, F, F@_1, F@_2, cons(NewFValue, Prev, TrUserData), F@_4, TrUserData). -d_pfield_Package_retired(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 57 -> d_pfield_Package_retired(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); -d_pfield_Package_retired(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, E, TrUserData) -> +d_pfield_Package_retired(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 57 -> d_pfield_Package_retired(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); +d_pfield_Package_retired(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, E, F@_4, TrUserData) -> Len = X bsl N + Acc, <> = Rest, NewSeq = d_packed_field_Package_retired(PackedBytes, 0, 0, F, E, TrUserData), - dfp_read_field_def_Package(Rest2, 0, 0, F, F@_1, F@_2, NewSeq, TrUserData). + dfp_read_field_def_Package(Rest2, 0, 0, F, F@_1, F@_2, NewSeq, F@_4, TrUserData). d_packed_field_Package_retired(<<1:1, X:7, Rest/binary>>, N, Acc, F, AccSeq, TrUserData) when N < 57 -> d_packed_field_Package_retired(Rest, N + 7, X bsl N + Acc, F, AccSeq, TrUserData); d_packed_field_Package_retired(<<0:1, X:7, Rest/binary>>, N, Acc, F, AccSeq, TrUserData) -> @@ -397,22 +422,40 @@ d_packed_field_Package_retired(<<0:1, X:7, Rest/binary>>, N, Acc, F, AccSeq, TrU d_packed_field_Package_retired(RestF, 0, 0, F, [NewFValue | AccSeq], TrUserData); d_packed_field_Package_retired(<<>>, 0, 0, _, AccSeq, _) -> AccSeq. -skip_varint_Package(<<1:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> skip_varint_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData); -skip_varint_Package(<<0:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> dfp_read_field_def_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData). +d_field_Package_with_advisories(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 57 -> d_field_Package_with_advisories(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); +d_field_Package_with_advisories(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, Prev, TrUserData) -> + {NewFValue, RestF} = {begin <> = <<(X bsl N + Acc):32/unsigned-native>>, id(Res, TrUserData) end, Rest}, + dfp_read_field_def_Package(RestF, 0, 0, F, F@_1, F@_2, F@_3, cons(NewFValue, Prev, TrUserData), TrUserData). -skip_length_delimited_Package(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 57 -> skip_length_delimited_Package(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); -skip_length_delimited_Package(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) -> +d_pfield_Package_with_advisories(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 57 -> d_pfield_Package_with_advisories(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); +d_pfield_Package_with_advisories(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, E, TrUserData) -> + Len = X bsl N + Acc, + <> = Rest, + NewSeq = d_packed_field_Package_with_advisories(PackedBytes, 0, 0, F, E, TrUserData), + dfp_read_field_def_Package(Rest2, 0, 0, F, F@_1, F@_2, F@_3, NewSeq, TrUserData). + +d_packed_field_Package_with_advisories(<<1:1, X:7, Rest/binary>>, N, Acc, F, AccSeq, TrUserData) when N < 57 -> d_packed_field_Package_with_advisories(Rest, N + 7, X bsl N + Acc, F, AccSeq, TrUserData); +d_packed_field_Package_with_advisories(<<0:1, X:7, Rest/binary>>, N, Acc, F, AccSeq, TrUserData) -> + {NewFValue, RestF} = {begin <> = <<(X bsl N + Acc):32/unsigned-native>>, id(Res, TrUserData) end, Rest}, + d_packed_field_Package_with_advisories(RestF, 0, 0, F, [NewFValue | AccSeq], TrUserData); +d_packed_field_Package_with_advisories(<<>>, 0, 0, _, AccSeq, _) -> AccSeq. + +skip_varint_Package(<<1:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> skip_varint_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData); +skip_varint_Package(<<0:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> dfp_read_field_def_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData). + +skip_length_delimited_Package(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 57 -> skip_length_delimited_Package(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); +skip_length_delimited_Package(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> Length = X bsl N + Acc, <<_:Length/binary, Rest2/binary>> = Rest, - dfp_read_field_def_Package(Rest2, 0, 0, F, F@_1, F@_2, F@_3, TrUserData). + dfp_read_field_def_Package(Rest2, 0, 0, F, F@_1, F@_2, F@_3, F@_4, TrUserData). -skip_group_Package(Bin, _, Z2, FNum, F@_1, F@_2, F@_3, TrUserData) -> +skip_group_Package(Bin, _, Z2, FNum, F@_1, F@_2, F@_3, F@_4, TrUserData) -> {_, Rest} = read_group(Bin, FNum), - dfp_read_field_def_Package(Rest, 0, Z2, FNum, F@_1, F@_2, F@_3, TrUserData). + dfp_read_field_def_Package(Rest, 0, Z2, FNum, F@_1, F@_2, F@_3, F@_4, TrUserData). -skip_32_Package(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> dfp_read_field_def_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData). +skip_32_Package(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> dfp_read_field_def_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData). -skip_64_Package(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> dfp_read_field_def_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData). +skip_64_Package(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> dfp_read_field_def_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData). read_group(Bin, FieldNum) -> {NumBytes, EndTagLen} = read_gr_b(Bin, 0, 0, 0, 0, FieldNum), @@ -500,11 +543,17 @@ merge_msg_Package(#{} = PMsg, #{name := NFname} = NMsg, TrUserData) -> {#{versions := PFversions}, _} -> S1#{versions => PFversions}; {_, _} -> S1 end, + S3 = case {PMsg, NMsg} of + {#{retired := PFretired}, #{retired := NFretired}} -> S2#{retired => 'erlang_++'(PFretired, NFretired, TrUserData)}; + {_, #{retired := NFretired}} -> S2#{retired => NFretired}; + {#{retired := PFretired}, _} -> S2#{retired => PFretired}; + {_, _} -> S2 + end, case {PMsg, NMsg} of - {#{retired := PFretired}, #{retired := NFretired}} -> S2#{retired => 'erlang_++'(PFretired, NFretired, TrUserData)}; - {_, #{retired := NFretired}} -> S2#{retired => NFretired}; - {#{retired := PFretired}, _} -> S2#{retired => PFretired}; - {_, _} -> S2 + {#{with_advisories := PFwith_advisories}, #{with_advisories := NFwith_advisories}} -> S3#{with_advisories => 'erlang_++'(PFwith_advisories, NFwith_advisories, TrUserData)}; + {_, #{with_advisories := NFwith_advisories}} -> S3#{with_advisories => NFwith_advisories}; + {#{with_advisories := PFwith_advisories}, _} -> S3#{with_advisories => PFwith_advisories}; + {_, _} -> S3 end. @@ -567,9 +616,19 @@ v_msg_Package(#{name := F1} = M, Path, TrUserData) -> end; _ -> ok end, + case M of + #{with_advisories := F4} -> + if is_list(F4) -> + _ = [v_type_int32(Elem, [with_advisories | Path], TrUserData) || Elem <- F4], + ok; + true -> mk_type_error({invalid_list_of, int32}, F4, [with_advisories | Path]) + end; + _ -> ok + end, lists:foreach(fun (name) -> ok; (versions) -> ok; (retired) -> ok; + (with_advisories) -> ok; (OtherKey) -> mk_type_error({extraneous_key, OtherKey}, M, Path) end, maps:keys(M)), @@ -636,7 +695,8 @@ get_msg_defs() -> {{msg, 'Package'}, [#{name => name, fnum => 1, rnum => 2, type => string, occurrence => required, opts => []}, #{name => versions, fnum => 2, rnum => 3, type => string, occurrence => repeated, opts => []}, - #{name => retired, fnum => 3, rnum => 4, type => int32, occurrence => repeated, opts => [packed]}]}]. + #{name => retired, fnum => 3, rnum => 4, type => int32, occurrence => repeated, opts => [packed]}, + #{name => with_advisories, fnum => 5, rnum => 5, type => int32, occurrence => repeated, opts => [packed]}]}]. get_msg_names() -> ['Versions', 'Package']. @@ -666,7 +726,8 @@ find_msg_def('Versions') -> [#{name => packages, fnum => 1, rnum => 2, type => { find_msg_def('Package') -> [#{name => name, fnum => 1, rnum => 2, type => string, occurrence => required, opts => []}, #{name => versions, fnum => 2, rnum => 3, type => string, occurrence => repeated, opts => []}, - #{name => retired, fnum => 3, rnum => 4, type => int32, occurrence => repeated, opts => [packed]}]; + #{name => retired, fnum => 3, rnum => 4, type => int32, occurrence => repeated, opts => [packed]}, + #{name => with_advisories, fnum => 5, rnum => 5, type => int32, occurrence => repeated, opts => [packed]}]; find_msg_def(_) -> error. diff --git a/apps/rebar/src/vendored/r3_hex_registry.erl b/apps/rebar/src/vendored/r3_hex_registry.erl index da66f7082..b82849c58 100644 --- a/apps/rebar/src/vendored/r3_hex_registry.erl +++ b/apps/rebar/src/vendored/r3_hex_registry.erl @@ -1,4 +1,4 @@ -%% Vendored from hex_core v0.15.0, do not edit manually +%% Vendored from hex_core v0.17.0, do not edit manually %% @doc %% Functions for encoding and decoding Hex registries. diff --git a/apps/rebar/src/vendored/r3_hex_repo.erl b/apps/rebar/src/vendored/r3_hex_repo.erl index d2085e22a..a9b6ed081 100644 --- a/apps/rebar/src/vendored/r3_hex_repo.erl +++ b/apps/rebar/src/vendored/r3_hex_repo.erl @@ -1,4 +1,4 @@ -%% Vendored from hex_core v0.15.0, do not edit manually +%% Vendored from hex_core v0.17.0, do not edit manually %% @doc %% Repo API. @@ -12,7 +12,9 @@ get_docs/3, get_docs_to_file/4, get_public_key/1, - get_hex_installs/1 + get_hex_installs/1, + fingerprint/1, + fingerprint_equal/2 ]). %%==================================================================== @@ -208,6 +210,66 @@ get_hex_installs(Config) -> Other end. +%% @doc +%% Computes a SHA256 fingerprint of a PEM-encoded public key. +%% +%% Returns a string in the format "SHA256:" followed by base64, which can be used +%% to verify public keys out-of-band. +%% +%% Examples: +%% +%% ``` +%% > r3_hex_repo:fingerprint(PublicKeyPem). +%% "SHA256:abc123..." +%% ''' +%% @end +-spec fingerprint(binary()) -> string(). +fingerprint(PublicKeyPem) when is_binary(PublicKeyPem) -> + [PemEntry] = public_key:pem_decode(PublicKeyPem), + PublicKey = public_key:pem_entry_decode(PemEntry), + application:ensure_all_started(ssh), + ssh:hostkey_fingerprint(sha256, PublicKey). + +%% @doc +%% Compares a PEM-encoded public key against an expected fingerprint. +%% +%% Uses constant-time comparison to prevent timing attacks. +%% +%% Examples: +%% +%% ``` +%% > r3_hex_repo:fingerprint_equal(PublicKeyPem, "SHA256:abc123..."). +%% true +%% ''' +%% @end +-spec fingerprint_equal(binary(), iodata()) -> boolean(). +fingerprint_equal(PublicKeyPem, ExpectedFingerprint) when is_binary(PublicKeyPem) -> + ActualFingerprint = fingerprint(PublicKeyPem), + constant_time_compare( + list_to_binary(ActualFingerprint), + iolist_to_binary(ExpectedFingerprint) + ). + +%% @private +%% Constant-time comparison to prevent timing attacks. +%% Uses crypto:hash_equals/2 on OTP 25+, falls back to manual comparison on older versions. +-if(?OTP_RELEASE >= 25). +constant_time_compare(A, B) when byte_size(A) =/= byte_size(B) -> + false; +constant_time_compare(A, B) -> + crypto:hash_equals(A, B). +-else. +constant_time_compare(A, B) when byte_size(A) =:= byte_size(B) -> + constant_time_compare(A, B, 0); +constant_time_compare(_, _) -> + false. + +constant_time_compare(<>, <>, Acc) -> + constant_time_compare(RestA, RestB, Acc bor (X bxor Y)); +constant_time_compare(<<>>, <<>>, Acc) -> + Acc =:= 0. +-endif. + %%==================================================================== %% Internal functions %%==================================================================== diff --git a/apps/rebar/src/vendored/r3_hex_safe_binary_to_term.erl b/apps/rebar/src/vendored/r3_hex_safe_binary_to_term.erl index cccc35ecb..a58495737 100644 --- a/apps/rebar/src/vendored/r3_hex_safe_binary_to_term.erl +++ b/apps/rebar/src/vendored/r3_hex_safe_binary_to_term.erl @@ -1,4 +1,4 @@ -%% Vendored from hex_core v0.15.0, do not edit manually +%% Vendored from hex_core v0.17.0, do not edit manually %% @hidden %% Safe deserialization of Erlang terms from binary. diff --git a/apps/rebar/src/vendored/r3_hex_tarball.erl b/apps/rebar/src/vendored/r3_hex_tarball.erl index b84834bd3..e8c24a96c 100644 --- a/apps/rebar/src/vendored/r3_hex_tarball.erl +++ b/apps/rebar/src/vendored/r3_hex_tarball.erl @@ -1,4 +1,4 @@ -%% Vendored from hex_core v0.15.0, do not edit manually +%% Vendored from hex_core v0.17.0, do not edit manually %% @doc %% Functions for creating and unpacking Hex tarballs. @@ -12,13 +12,14 @@ format_error/1 ]). -ifdef(TEST). --export([do_decode_metadata/1, gzip/1, normalize_requirements/1]). +-export([do_decode_metadata/1, do_decode_metadata/2, gzip/1, normalize_requirements/1]). -endif. -define(VERSION, <<"3">>). -define(HASH_CHUNK_SIZE, 65536). -define(MAX_VERSION_SIZE, 32). -define(MAX_CHECKSUM_SIZE, 128). --define(MAX_METADATA_SIZE, 128 * 1024). +-define(MAX_METADATA_SIZE, 1024 * 1024). +-define(METADATA_CHUNK_SIZE, 4096). -define(BUILD_TOOL_FILES, [ {<<"mix.exs">>, <<"mix">>}, {<<"rebar.config">>, <<"rebar3">>}, @@ -68,6 +69,7 @@ create(Metadata, Files, Config) -> tarball_max_size := TarballMaxSize, tarball_max_uncompressed_size := TarballMaxUncompressedSize } = Config, + FilesRoot = maps:get(tarball_files_root, Config, "."), MetadataBinary = encode_metadata(Metadata), @@ -75,35 +77,42 @@ create(Metadata, Files, Config) -> false -> {error, {tarball, {file_too_big, "metadata.config"}}}; true -> - ContentsTarball = create_memory_tarball(Files), - ContentsTarballCompressed = gzip(ContentsTarball), - InnerChecksum = inner_checksum(?VERSION, MetadataBinary, ContentsTarballCompressed), - InnerChecksumBase16 = encode_base16(InnerChecksum), - - OuterFiles = [ - {"VERSION", ?VERSION}, - {"CHECKSUM", InnerChecksumBase16}, - {"metadata.config", MetadataBinary}, - {"contents.tar.gz", ContentsTarballCompressed} - ], - - case valid_size(ContentsTarball, TarballMaxUncompressedSize) of - true -> - Tarball = create_memory_tarball(OuterFiles), - OuterChecksum = checksum(Tarball), + case validate_create_files(Files, FilesRoot) of + {ok, ValidatedFiles} -> + ContentsTarball = create_memory_tarball(ValidatedFiles), + ContentsTarballCompressed = gzip(ContentsTarball), + InnerChecksum = inner_checksum( + ?VERSION, MetadataBinary, ContentsTarballCompressed + ), + InnerChecksumBase16 = encode_base16(InnerChecksum), + + OuterFiles = [ + {"VERSION", ?VERSION}, + {"CHECKSUM", InnerChecksumBase16}, + {"metadata.config", MetadataBinary}, + {"contents.tar.gz", ContentsTarballCompressed} + ], - case valid_size(Tarball, TarballMaxSize) of + case valid_size(ContentsTarball, TarballMaxUncompressedSize) of true -> - {ok, #{ - tarball => Tarball, - outer_checksum => OuterChecksum, - inner_checksum => InnerChecksum - }}; + Tarball = create_memory_tarball(OuterFiles), + OuterChecksum = checksum(Tarball), + + case valid_size(Tarball, TarballMaxSize) of + true -> + {ok, #{ + tarball => Tarball, + outer_checksum => OuterChecksum, + inner_checksum => InnerChecksum + }}; + false -> + {error, {tarball, {too_big_compressed, TarballMaxSize}}} + end; false -> - {error, {tarball, {too_big_compressed, TarballMaxSize}}} + {error, {tarball, {too_big_uncompressed, TarballMaxUncompressedSize}}} end; - false -> - {error, {tarball, {too_big_uncompressed, TarballMaxUncompressedSize}}} + {error, _} = Error -> + Error end end. @@ -134,21 +143,27 @@ create_docs(Files, Config) -> docs_tarball_max_size := TarballMaxSize, docs_tarball_max_uncompressed_size := TarballMaxUncompressedSize } = Config, + FilesRoot = maps:get(tarball_files_root, Config, "."), - UncompressedTarball = create_memory_tarball(Files), + case validate_create_files(Files, FilesRoot) of + {ok, ValidatedFiles} -> + UncompressedTarball = create_memory_tarball(ValidatedFiles), - case valid_size(UncompressedTarball, TarballMaxUncompressedSize) of - true -> - Tarball = gzip(UncompressedTarball), - - case valid_size(Tarball, TarballMaxSize) of + case valid_size(UncompressedTarball, TarballMaxUncompressedSize) of true -> - {ok, Tarball}; + Tarball = gzip(UncompressedTarball), + + case valid_size(Tarball, TarballMaxSize) of + true -> + {ok, Tarball}; + false -> + {error, {tarball, {too_big_compressed, TarballMaxSize}}} + end; false -> - {error, {tarball, {too_big_compressed, TarballMaxSize}}} + {error, {tarball, {too_big_uncompressed, TarballMaxUncompressedSize}}} end; - false -> - {error, {tarball, {too_big_uncompressed, TarballMaxUncompressedSize}}} + {error, _} = Error -> + Error end. -spec create_docs(files()) -> {ok, tarball()} | {error, term()}. @@ -345,6 +360,12 @@ format_error({tarball, {bad_version, Vsn}}) -> io_lib:format("unsupported version: ~p", [Vsn]); format_error({tarball, invalid_checksum}) -> "invalid tarball checksum"; +format_error({tarball, {unsafe_path, Name}}) -> + io_lib:format("unsafe path in tarball: ~s", [Name]); +format_error({tarball, {unsafe_symlink, Name, LinkTarget}}) -> + io_lib:format("unsafe symlink in tarball: ~s -> ~s", [Name, LinkTarget]); +format_error({tarball, {unsupported_file_type, Name, Type}}) -> + io_lib:format("unsupported file type in tarball: ~s (~p)", [Name, Type]); format_error({tarball, Reason}) -> "tarball error, " ++ r3_hex_erl_tar:format_error(Reason); format_error({inner_tarball, Reason}) -> @@ -548,41 +569,291 @@ check_inner_checksum(#{files := Files} = State) -> %% @private decode_metadata({error, _} = Error) -> Error; -decode_metadata(#{files := #{"metadata.config" := Binary}} = State) when is_binary(Binary) -> - case do_decode_metadata(Binary) of +decode_metadata(#{files := #{"metadata.config" := Binary}, config := Config} = State) when + is_binary(Binary) +-> + Fields = maps:get(metadata_fields, Config, all), + case do_decode_metadata(Binary, Fields) of #{} = Metadata -> maps:put(metadata, normalize_metadata(Metadata), State); Other -> Other end. +-ifdef(TEST). +do_decode_metadata(Binary) -> + do_decode_metadata(Binary, all). +-endif. + %% @private -do_decode_metadata(Binary) when is_binary(Binary) -> - {ok, String} = characters_to_list(Binary), +do_decode_metadata(Binary, all) when is_binary(Binary) -> + case decode_metadata_chunked(utf8, Binary, <<>>, [], "", []) of + latin1_fallback -> + decode_metadata_chunked(latin1, Binary, <<>>, [], "", []); + Other -> + Other + end; +do_decode_metadata(Binary, Fields) when is_binary(Binary), is_list(Fields) -> + case decode_metadata_streaming(utf8, Binary, <<>>, [], "", [], Fields, start) of + latin1_fallback -> + decode_metadata_streaming(latin1, Binary, <<>>, [], "", [], Fields, start); + Other -> + Other + end. - case r3_safe_erl_term:string(String) of - {ok, Tokens, _Line} -> - try - Terms = r3_safe_erl_term:terms(Tokens), - maps:from_list(Terms) - catch - error:function_clause -> - {error, {metadata, invalid_terms}}; - error:badarg -> - {error, {metadata, not_key_value}} +%% @private +%% Streams the metadata.config binary through r3_safe_erl_term:tokens/2 in +%% small chunks so we never materialize the whole binary as a char list. +%% Each accepted dot-terminated form is parsed and accumulated immediately, +%% keeping peak memory at roughly one chunk + one term's tokens + AST. +decode_metadata_chunked(Encoding, Binary, IncTail, Cont, Chars, Acc) -> + case Chars of + [] when Binary =:= <<>>, IncTail =:= <<>> -> + flush_metadata_eof(Cont, Acc); + [] when Binary =:= <<>>, Encoding =:= utf8 -> + %% Trailing bytes that can never form a complete UTF-8 sequence — + %% restart the whole decode in latin1 mode rather than spin. + latin1_fallback; + [] -> + case decode_metadata_chunk(Encoding, Binary, IncTail) of + {ok, NewChars, NewBinary, NewTail} -> + feed_metadata(Encoding, Cont, NewChars, NewBinary, NewTail, Acc); + latin1_fallback -> + latin1_fallback + end; + _ -> + feed_metadata(Encoding, Cont, Chars, Binary, IncTail, Acc) + end. + +%% @private +feed_metadata(Encoding, Cont, Chars, Binary, IncTail, Acc) -> + case r3_safe_erl_term:tokens(Cont, Chars) of + {more, NewCont} -> + decode_metadata_chunked(Encoding, Binary, IncTail, NewCont, "", Acc); + {done, {ok, Tokens, _}, RestChars} -> + case parse_metadata_term(Tokens) of + {ok, Term} -> + decode_metadata_chunked( + Encoding, Binary, IncTail, [], normalize_rest_chars(RestChars), [Term | Acc] + ); + {error, _} = Err -> + Err end; - {error, {_Line, r3_safe_erl_term, Reason}, _Line2} -> + {done, {eof, _}, _} -> + finalize_metadata(Acc); + {done, {error, {_, r3_safe_erl_term, Reason}, _}, _} -> {error, {metadata, Reason}} end. %% @private -characters_to_list(Binary) -> - case unicode:characters_to_list(Binary) of - List when is_list(List) -> - {ok, List}; +flush_metadata_eof([], Acc) -> + finalize_metadata(Acc); +flush_metadata_eof(Cont, Acc) -> + case r3_safe_erl_term:tokens(Cont, eof) of + {done, {eof, _}, _} -> + finalize_metadata(Acc); + {done, {ok, _Tokens, _}, _} -> + {error, {metadata, invalid_terms}}; + {done, {error, {_, r3_safe_erl_term, Reason}, _}, _} -> + {error, {metadata, Reason}} + end. + +%% @private +finalize_metadata([]) -> + {error, {metadata, invalid_terms}}; +finalize_metadata(Acc) -> + try maps:from_list(lists:reverse(Acc)) of + Map -> Map + catch + error:badarg -> {error, {metadata, not_key_value}} + end. + +%% @private +parse_metadata_term(Tokens) -> + case erl_parse:parse_term(Tokens) of + {ok, Term} -> {ok, Term}; + {error, _} -> {error, {metadata, invalid_terms}} + end. + +%% @private +decode_metadata_chunk(utf8, Binary, IncTail) -> + {Chunk, Rest} = take_metadata_chunk(Binary), + Combined = + case IncTail of + <<>> -> Chunk; + _ -> <> + end, + case unicode:characters_to_list(Combined, utf8) of + L when is_list(L) -> + {ok, L, Rest, <<>>}; + {incomplete, L, NewTail} -> + {ok, L, Rest, NewTail}; {error, _, _} -> - case unicode:characters_to_list(Binary, latin1) of - List when is_list(List) -> {ok, List}; - Other -> Other - end + latin1_fallback + end; +decode_metadata_chunk(latin1, Binary, _IncTail) -> + {Chunk, Rest} = take_metadata_chunk(Binary), + {ok, binary_to_list(Chunk), Rest, <<>>}. + +%% @private +take_metadata_chunk(Binary) when byte_size(Binary) > ?METADATA_CHUNK_SIZE -> + <> = Binary, + {Chunk, Rest}; +take_metadata_chunk(Binary) -> + {Binary, <<>>}. + +%% @private +normalize_rest_chars(eof) -> ""; +normalize_rest_chars(L) when is_list(L) -> L. + +%% @private +%% Streams the metadata.config binary through r3_safe_erl_term:token/2 one token +%% at a time. Forms whose key is in Fields are accumulated and parsed; forms +%% whose key is not in Fields are discarded with only a depth counter held in +%% state, so peak memory stays bounded regardless of the unwanted form's size. +decode_metadata_streaming(Encoding, Binary, IncTail, Cont, Chars, Acc, Fields, State) -> + case Chars of + [] when Binary =:= <<>>, IncTail =:= <<>> -> + flush_metadata_streaming_eof(Cont, Acc, Fields, State); + [] when Binary =:= <<>>, Encoding =:= utf8 -> + latin1_fallback; + [] -> + case decode_metadata_chunk(Encoding, Binary, IncTail) of + {ok, NewChars, NewBinary, NewTail} -> + feed_metadata_streaming( + Encoding, Cont, NewChars, NewBinary, NewTail, Acc, Fields, State + ); + latin1_fallback -> + latin1_fallback + end; + _ -> + feed_metadata_streaming(Encoding, Cont, Chars, Binary, IncTail, Acc, Fields, State) + end. + +%% @private +feed_metadata_streaming(Encoding, Cont, Chars, Binary, IncTail, Acc, Fields, State) -> + case r3_safe_erl_term:token(Cont, Chars) of + {more, NewCont} -> + decode_metadata_streaming(Encoding, Binary, IncTail, NewCont, "", Acc, Fields, State); + {done, {ok, Token, _}, RestChars} -> + case advance_metadata_state(State, Acc, Fields, Token) of + {next, NewState, NewAcc} -> + decode_metadata_streaming( + Encoding, + Binary, + IncTail, + [], + normalize_rest_chars(RestChars), + NewAcc, + Fields, + NewState + ); + {error, _} = Err -> + Err + end; + {done, {eof, _}, _} -> + finalize_metadata_streaming(Acc, State); + {done, {error, {_, r3_safe_erl_term, Reason}, _}, _} -> + {error, {metadata, Reason}} + end. + +%% @private +flush_metadata_streaming_eof([], Acc, _Fields, State) -> + finalize_metadata_streaming(Acc, State); +flush_metadata_streaming_eof(Cont, Acc, Fields, State) -> + case r3_safe_erl_term:token(Cont, eof) of + {done, {ok, Token, _}, _} -> + case advance_metadata_state(State, Acc, Fields, Token) of + {next, NewState, NewAcc} -> + flush_metadata_streaming_eof([], NewAcc, Fields, NewState); + {error, _} = Err -> + Err + end; + {done, {eof, _}, _} -> + finalize_metadata_streaming(Acc, State); + {done, {error, {_, r3_safe_erl_term, Reason}, _}, _} -> + {error, {metadata, Reason}} + end. + +%% @private +finalize_metadata_streaming(Acc, start) -> + finalize_metadata(Acc); +finalize_metadata_streaming([], between) -> + #{}; +finalize_metadata_streaming(Acc, between) -> + finalize_metadata(Acc); +finalize_metadata_streaming(_Acc, _State) -> + {error, {metadata, invalid_terms}}. + +%% @private +%% State machine for streaming the metadata.config schema. Forms are required +%% to be `{<<"key">>, value}.` — anything else is rejected as invalid. +%% +%% States: start | between | {after_open, Prefix} | {after_left_binary, Prefix} +%% | {after_key, KeyChars, Prefix} | {after_right_binary, KeyChars, Prefix} +%% | {accumulate, Prefix, Depth} | {skip, Depth} +%% +%% `start` is the initial position; `between` is the position after a form has +%% been completed. Distinguishing them lets empty input return the same +%% invalid_terms error as the non-streaming path while a stream that +%% successfully skipped every form returns an empty map. +advance_metadata_state(Open, Acc, _Fields, {'{', _} = T) when Open =:= start; Open =:= between -> + {next, {after_open, [T]}, Acc}; +advance_metadata_state({after_open, Prefix}, Acc, _Fields, {'<<', _} = T) -> + {next, {after_left_binary, [T | Prefix]}, Acc}; +advance_metadata_state({after_left_binary, Prefix}, Acc, _Fields, {string, _, KeyChars} = T) -> + {next, {after_key, KeyChars, [T | Prefix]}, Acc}; +advance_metadata_state({after_key, KeyChars, Prefix}, Acc, _Fields, {'>>', _} = T) -> + {next, {after_right_binary, KeyChars, [T | Prefix]}, Acc}; +advance_metadata_state({after_right_binary, KeyChars, Prefix}, Acc, Fields, {',', _} = T) -> + case extract_metadata_key(KeyChars) of + {ok, Key} -> + case lists:member(Key, Fields) of + true -> {next, {accumulate, [T | Prefix], 1}, Acc}; + false -> {next, {skip, 1}, Acc} + end; + error -> + {error, {metadata, not_key_value}} + end; +advance_metadata_state({accumulate, Prefix, 0}, Acc, _Fields, {dot, _} = T) -> + Tokens = lists:reverse([T | Prefix]), + case parse_metadata_term(Tokens) of + {ok, Term} -> {next, between, [Term | Acc]}; + {error, _} = Err -> Err + end; +advance_metadata_state({accumulate, _, _}, _Acc, _Fields, {dot, _}) -> + {error, {metadata, invalid_terms}}; +advance_metadata_state({accumulate, Prefix, Depth}, Acc, _Fields, {Open, _} = T) when + Open =:= '{'; Open =:= '[' +-> + {next, {accumulate, [T | Prefix], Depth + 1}, Acc}; +advance_metadata_state({accumulate, Prefix, Depth}, Acc, _Fields, {Close, _} = T) when + Close =:= '}'; Close =:= ']' +-> + {next, {accumulate, [T | Prefix], Depth - 1}, Acc}; +advance_metadata_state({accumulate, Prefix, Depth}, Acc, _Fields, T) -> + {next, {accumulate, [T | Prefix], Depth}, Acc}; +advance_metadata_state({skip, 0}, Acc, _Fields, {dot, _}) -> + {next, between, Acc}; +advance_metadata_state({skip, _}, _Acc, _Fields, {dot, _}) -> + {error, {metadata, invalid_terms}}; +advance_metadata_state({skip, Depth}, Acc, _Fields, {Open, _}) when + Open =:= '{'; Open =:= '[' +-> + {next, {skip, Depth + 1}, Acc}; +advance_metadata_state({skip, Depth}, Acc, _Fields, {Close, _}) when + Close =:= '}'; Close =:= ']' +-> + {next, {skip, Depth - 1}, Acc}; +advance_metadata_state({skip, Depth}, Acc, _Fields, _Token) -> + {next, {skip, Depth}, Acc}; +advance_metadata_state(_State, _Acc, _Fields, _Token) -> + {error, {metadata, not_key_value}}. + +%% @private +extract_metadata_key(KeyChars) -> + try list_to_binary(KeyChars) of + Key -> {ok, Key} + catch + error:badarg -> error end. %% @private @@ -631,6 +902,193 @@ guess_build_tools(Metadata) -> %% Tar Helpers %%==================================================================== +%% @private +validate_create_files(Files, FilesRoot) when is_list(Files) -> + validate_create_files(Files, FilesRoot, []). + +validate_create_files([], _FilesRoot, Acc) -> + {ok, lists:reverse(Acc)}; +validate_create_files([File | Rest], FilesRoot, Acc) -> + case validate_create_file(File, FilesRoot) of + {ok, ValidatedFile} -> validate_create_files(Rest, FilesRoot, [ValidatedFile | Acc]); + {error, _} = Error -> Error + end. + +validate_create_file({Filename, Contents}, _FilesRoot) when + is_list(Filename), is_binary(Contents) +-> + case validate_archive_path(Filename) of + ok -> {ok, {Filename, Contents}}; + {error, _} = Error -> Error + end; +validate_create_file(Filename, FilesRoot) when is_list(Filename) -> + validate_create_file({Filename, Filename}, FilesRoot); +validate_create_file({Filename, AbsFilename}, FilesRoot) when + is_list(Filename), is_list(AbsFilename) +-> + case validate_archive_path(Filename) of + ok -> validate_source_file(Filename, AbsFilename, FilesRoot); + {error, _} = Error -> Error + end. + +validate_archive_path(Filename) -> + case safe_relative_archive_path(Filename) of + false -> {error, {tarball, {unsafe_path, Filename}}}; + true -> ok + end. + +validate_source_file(ArchiveName, SourcePath, FilesRoot) -> + case source_file_paths(SourcePath, FilesRoot) of + {ok, DiskPath, RelativePath, Root} -> + validate_source_file_root(ArchiveName, DiskPath, RelativePath, Root); + outside_root -> + {error, {tarball, {unsafe_path, ArchiveName}}} + end. + +source_file_paths(SourcePath, FilesRoot) -> + Root = normalize_root(filename:absname(FilesRoot)), + case source_relative_path(SourcePath, Root) of + {ok, RelativePath} -> + {ok, source_disk_path(SourcePath, Root), RelativePath, Root}; + outside_root -> + outside_root + end. + +normalize_root(Path) -> + filename:join(normalize_root_parts(filename:split(Path), [])). + +normalize_root_parts([], Acc) -> + lists:reverse(Acc); +normalize_root_parts(["." | Parts], Acc) -> + normalize_root_parts(Parts, Acc); +normalize_root_parts([".." | Parts], Acc) -> + normalize_root_parent(Parts, Acc); +normalize_root_parts([Part | Parts], Acc) -> + normalize_root_parts(Parts, [Part | Acc]). + +normalize_root_parent(Parts, [Root] = Acc) -> + case filename:pathtype(Root) of + relative -> normalize_root_parts(Parts, []); + _ -> normalize_root_parts(Parts, Acc) + end; +normalize_root_parent(Parts, [_Part | Acc]) -> + normalize_root_parts(Parts, Acc); +normalize_root_parent(Parts, []) -> + normalize_root_parts(Parts, []). + +source_disk_path(SourcePath, Root) -> + case filename:pathtype(SourcePath) of + absolute -> SourcePath; + _ -> filename:join(Root, SourcePath) + end. + +source_relative_path(SourcePath, Root) -> + case filename:pathtype(SourcePath) of + absolute -> strip_root_path(filename:split(SourcePath), filename:split(Root)); + _ -> {ok, SourcePath} + end. + +strip_root_path([], []) -> + {ok, "."}; +strip_root_path(PathParts, []) -> + {ok, filename:join(PathParts)}; +strip_root_path([Part | PathParts], [Part | RootParts]) -> + strip_root_path(PathParts, RootParts); +strip_root_path(_PathParts, _RootParts) -> + outside_root. + +validate_source_file_root(ArchiveName, DiskPath, RelativePath, Root) -> + case file:read_link_info(DiskPath, []) of + {ok, #file_info{type = Type}} when Type =:= regular; Type =:= directory -> + case validate_source_root(ArchiveName, RelativePath, Root) of + ok -> {ok, {ArchiveName, DiskPath}}; + {error, _} = Error -> Error + end; + {ok, #file_info{type = symlink}} -> + {ok, LinkTarget} = file:read_link(DiskPath), + ResolvedTarget = archive_join(archive_dirname(ArchiveName), LinkTarget), + case safe_relative_archive_path(ResolvedTarget) of + false -> + {error, {tarball, {unsafe_symlink, ArchiveName, LinkTarget}}}; + true -> + case validate_source_root(ArchiveName, RelativePath, Root) of + ok -> {ok, {ArchiveName, DiskPath}}; + {error, _} = Error -> Error + end + end; + {ok, #file_info{type = Type}} -> + {error, {tarball, {unsupported_file_type, ArchiveName, Type}}}; + _ -> + case validate_source_root(ArchiveName, RelativePath, Root) of + ok -> {ok, {ArchiveName, DiskPath}}; + {error, _} = Error -> Error + end + end. + +validate_source_root(ArchiveName, SourcePath, FilesRoot) -> + case filelib:safe_relative_path(SourcePath, FilesRoot) of + unsafe -> {error, {tarball, {unsafe_path, ArchiveName}}}; + _ -> ok + end. + +safe_relative_archive_path(Path) -> + case archive_path_absolute(Path) orelse archive_path_drive(Path) of + true -> false; + false -> safe_relative_archive_path(archive_path_split(Path), []) + end. + +safe_relative_archive_path([], _Acc) -> + true; +safe_relative_archive_path(["." | Rest], Acc) -> + safe_relative_archive_path(Rest, Acc); +safe_relative_archive_path([".." | _Rest], []) -> + false; +safe_relative_archive_path([".." | Rest], [_ | Acc]) -> + safe_relative_archive_path(Rest, Acc); +safe_relative_archive_path([_Part | Rest], Acc) -> + safe_relative_archive_path(Rest, [ok | Acc]). + +archive_path_absolute([$/ | _Rest]) -> + true; +archive_path_absolute([$\\ | _Rest]) -> + true; +archive_path_absolute(_Path) -> + false. + +archive_path_drive([Drive, $: | _Rest]) when + Drive >= $a, Drive =< $z; + Drive >= $A, Drive =< $Z +-> + true; +archive_path_drive(_Path) -> + false. + +archive_path_split(Path) -> + string:tokens(Path, "/\\"). + +archive_dirname(Path) -> + case archive_path_split(Path) of + [] -> "."; + [_Name] -> "."; + Parts -> string:join(lists:droplast(Parts), "/") + end. + +archive_join(_Dir, Path) when Path =:= [] -> + Path; +archive_join(_Dir, Path = [$/ | _Rest]) -> + Path; +archive_join(_Dir, Path = [$\\ | _Rest]) -> + Path; +archive_join(_Dir, Path = [Drive, $: | _Rest]) when + Drive >= $a, Drive =< $z; + Drive >= $A, Drive =< $Z +-> + Path; +archive_join(".", Path) -> + Path; +archive_join(Dir, Path) -> + Dir ++ "/" ++ Path. + %% @private unpack_tarball(Source, memory, MaxSize) -> case r3_hex_erl_tar:extract(Source, [memory, compressed, {max_size, MaxSize}]) of diff --git a/apps/rebar/src/vendored/r3_safe_erl_term.erl b/apps/rebar/src/vendored/r3_safe_erl_term.erl index a54aca467..811feed56 100644 --- a/apps/rebar/src/vendored/r3_safe_erl_term.erl +++ b/apps/rebar/src/vendored/r3_safe_erl_term.erl @@ -45,7 +45,7 @@ -export([format_error/1]). %% User code. This is placed here to allow extra attributes. --file("r3_safe_erl_term.xrl", 26). +-file("r3_safe_erl_term.xrl", 28). -export([terms/1]). @@ -742,17 +742,17 @@ yyaction_5(TokenChars, TokenLine) -> { token, { list_to_atom (TokenChars), TokenLine } } . -compile({inline,yyaction_6/1}). --file("r3_safe_erl_term.xrl", 20). +-file("r3_safe_erl_term.xrl", 22). yyaction_6(TokenLine) -> - { token, { dot, TokenLine } } . + { end_token, { dot, TokenLine } } . -compile({inline,yyaction_7/1}). --file("r3_safe_erl_term.xrl", 21). +-file("r3_safe_erl_term.xrl", 23). yyaction_7(TokenLine) -> { token, { '/', TokenLine } } . -compile({inline,yyaction_8/0}). --file("r3_safe_erl_term.xrl", 22). +-file("r3_safe_erl_term.xrl", 24). yyaction_8() -> skip_token . -file("leexinc.hrl", 377). diff --git a/apps/rebar/src/vendored/r3_safe_erl_term.xrl b/apps/rebar/src/vendored/r3_safe_erl_term.xrl index f932f789b..2c7b9f81d 100644 --- a/apps/rebar/src/vendored/r3_safe_erl_term.xrl +++ b/apps/rebar/src/vendored/r3_safe_erl_term.xrl @@ -1,4 +1,4 @@ -%% Vendored from hex_core v0.15.0, do not edit manually +%% Vendored from hex_core v0.17.0, do not edit manually %%% Author : Robert Virding %%% Purpose : Token definitions for Erlang. @@ -19,7 +19,9 @@ Rules. {D}+ : {token, {integer, TokenLine, list_to_integer(TokenChars)}}. [\#\[\]}{,+-] : {token, {list_to_atom(TokenChars), TokenLine}}. (<<|>>|=>) : {token, {list_to_atom(TokenChars), TokenLine}}. -\. : {token, {dot, TokenLine}}. +% end_token (not token) lets r3_hex_tarball stream-decode metadata.config +% one form at a time via r3_safe_erl_term:tokens/2. +\. : {end_token, {dot, TokenLine}}. / : {token, {'/', TokenLine}}. {WS}+ : skip_token. diff --git a/apps/rebar/test/mock_pkg_resource.erl b/apps/rebar/test/mock_pkg_resource.erl index f609b5f36..cb2f1fb4e 100644 --- a/apps/rebar/test/mock_pkg_resource.erl +++ b/apps/rebar/test/mock_pkg_resource.erl @@ -101,7 +101,8 @@ mock_download(Opts) -> <<"version">> => Vsn}, Files = all_files(rebar_app_info:dir(AppInfo1)), - {ok, #{tarball := Tarball}} = r3_hex_tarball:create(Metadata, archive_names(Dir, Files)), + HexConfig = r3_hex_core:default_config(), + {ok, #{tarball := Tarball}} = r3_hex_tarball:create(Metadata, archive_names(Dir, Files), HexConfig#{tarball_files_root => Dir}), Archive = filename:join([Dir, TarApp]), file:write_file(Archive, Tarball), diff --git a/apps/rebar/test/rebar_pkg_SUITE.erl b/apps/rebar/test/rebar_pkg_SUITE.erl index 6a74fcb96..007502291 100644 --- a/apps/rebar/test/rebar_pkg_SUITE.erl +++ b/apps/rebar/test/rebar_pkg_SUITE.erl @@ -17,7 +17,8 @@ all() -> [good_uncached, good_cached, badpkg, badhash_nocache, badindexchk, badhash_cache, bad_to_good, good_disconnect, bad_disconnect, pkgs_provider, find_highest_matching, - parse_deps_ignores_optional]. + parse_deps_ignores_optional, + oauth_download_with_token, oauth_download_token_refresh]. init_per_suite(Config) -> application:start(meck), @@ -102,6 +103,20 @@ init_per_testcase(bad_disconnect=Name, Config0) -> {error, econnrefused} end), Config; +init_per_testcase(oauth_download_with_token=Name, Config0) -> + Config = [{good_cache, false}, + {pkg, {<<"goodpkg">>, <<"1.0.0">>}}, + {oauth_token, <<"test-oauth-token">>} + | Config0], + mock_config_oauth(Name, Config); +init_per_testcase(oauth_download_token_refresh=Name, Config0) -> + Config = [{good_cache, false}, + {pkg, {<<"goodpkg">>, <<"1.0.0">>}}, + {oauth_token, <<"expired-token">>}, + {oauth_refresh_token, <<"refresh-token">>}, + {new_oauth_token, <<"new-access-token">>} + | Config0], + mock_config_oauth_refresh(Name, Config); init_per_testcase(Name, Config0) -> Config = [{good_cache, false}, {pkg, {<<"goodpkg">>, <<"1.0.0">>}} @@ -268,6 +283,44 @@ parse_deps_ignores_optional(_Config) -> {<<"alias">>, {pkg, <<"ali">>, <<"4.0">>, _, _}}], Result). +%% Test that OAuth token is properly sent as Bearer token in requests +oauth_download_with_token(Config) -> + Tmp = ?config(tmp_dir, Config), + {Pkg, Vsn} = ?config(pkg, Config), + State = ?config(state, Config), + OAuthToken = ?config(oauth_token, Config), + ExpectedBearer = <<"Bearer ", OAuthToken/binary>>, + ?assertEqual(ok, + rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, ?good_checksum, ?good_checksum, + #{name => <<"hexpm">>, trusted => true}}, + State, #{}, true)), + %% Verify that the Bearer token was sent + ?assert(meck:called(r3_hex_repo, get_tarball, + [meck:is(fun(#{repo_key := Key}) -> Key =:= ExpectedBearer end), '_', '_'])), + Cache = ?config(cache_dir, Config), + ?assert(filelib:is_regular(filename:join(Cache, <>))). + +%% Test that OAuth token is refreshed on 401 and request is retried +oauth_download_token_refresh(Config) -> + Tmp = ?config(tmp_dir, Config), + {Pkg, Vsn} = ?config(pkg, Config), + State = ?config(state, Config), + _OldToken = ?config(oauth_token, Config), + _RefreshToken = ?config(oauth_refresh_token, Config), + NewToken = ?config(new_oauth_token, Config), + NewBearer = <<"Bearer ", NewToken/binary>>, + ?assertEqual(ok, + rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, ?good_checksum, ?good_checksum, + #{name => <<"hexpm">>, trusted => true}}, + State, #{}, true)), + %% Verify that refresh was called + ?assert(meck:called(r3_hex_api_oauth, refresh_token, '_')), + %% Verify that the new Bearer token was used for retry + ?assert(meck:called(r3_hex_repo, get_tarball, + [meck:is(fun(#{repo_key := Key}) -> Key =:= NewBearer end), '_', '_'])), + Cache = ?config(cache_dir, Config), + ?assert(filelib:is_regular(filename:join(Cache, <>))). + %%%%%%%%%%%%%%% %%% Helpers %%% %%%%%%%%%%%%%%% @@ -372,3 +425,214 @@ copy_to_cache({Pkg,Vsn}, Config) -> Source = filename:join(?config(data_dir, Config), Name), Dest = filename:join(?config(cache_dir, Config), Name), ec_file:copy(Source, Dest). + +%% Mock config for OAuth tests - similar to mock_config but includes OAuth token handling +mock_config_oauth(Name, Config) -> + Priv = ?config(priv_dir, Config), + CacheRoot = filename:join([Priv, "cache", atom_to_list(Name)]), + TmpDir = filename:join([Priv, "tmp", atom_to_list(Name)]), + Tid = ets:new(registry_table, [public]), + AllDeps = [ + {{<<"goodpkg">>,<<"1.0.0">>}, [[], ?good_checksum, ?good_checksum, [<<"rebar3">>]]} + ], + ets:insert_new(Tid, AllDeps), + CacheDir = filename:join([CacheRoot, "hex", "com", "test", "packages"]), + filelib:ensure_dir(filename:join([CacheDir, "registry"])), + ok = ets:tab2file(Tid, filename:join([CacheDir, "registry"])), + + catch ets:delete(?PACKAGE_TABLE), + rebar_packages:new_package_table(), + lists:foreach(fun({{N, Vsn}, [Deps, InnerChecksum, OuterChecksum, _]}) -> + {ok, Parsed} = rebar_semver:parse_version(Vsn), + ets:insert(?PACKAGE_TABLE, #package{key={ec_cnv:to_binary(N), Parsed, <<"hexpm">>}, + dependencies=Deps, + retired=false, + inner_checksum=InnerChecksum, + outer_checksum=OuterChecksum}) + end, AllDeps), + + meck:new(r3_hex_repo, [passthrough]), + meck:expect(r3_hex_repo, get_package, + fun(_Config, PkgName) -> + Matches = ets:match_object(Tid, {{PkgName,'_'}, '_'}), + Releases = + [#{outer_checksum => OuterChecksum, + inner_checksum => InnerChecksum, + version => Vsn, + dependencies => Deps} || + {{_, Vsn}, [Deps, InnerChecksum, OuterChecksum, _]} <- Matches], + {ok, {200, #{}, Releases}} + end), + + meck:new(rebar_state, [passthrough]), + meck:expect(rebar_state, get, + fun(_State, rebar_packages_cdn, _Default) -> + "http://test.com/"; + (_, _, Default) -> + Default + end), + meck:expect(rebar_state, resources, + fun(_State) -> + DefaultConfig = r3_hex_core:default_config(), + %% trusted => true is required for OAuth token resolution + [rebar_resource_v2:new(pkg, rebar_pkg_resource, + #{repos => [DefaultConfig#{name => <<"hexpm">>, + trusted => true}], + base_config => #{}})] + end), + + meck:new(rebar_dir, [passthrough]), + meck:expect(rebar_dir, global_cache_dir, fun(_) -> CacheRoot end), + + meck:expect(rebar_packages, registry_dir, fun(_) -> {ok, CacheDir} end), + meck:expect(rebar_packages, package_dir, fun(_, _) -> {ok, CacheDir} end), + + meck:new(rebar_prv_update, [passthrough]), + meck:expect(rebar_prv_update, do, fun(State) -> {ok, State} end), + + {Pkg, Vsn} = ?config(pkg, Config), + PkgFile = <>, + {ok, PkgContents} = file:read_file(filename:join(?config(data_dir, Config), PkgFile)), + + %% Write auth config file with OAuth token + OAuthToken = ?config(oauth_token, Config), + AuthConfigDir = filename:join([CacheRoot, "config"]), + AuthConfigFile = filename:join(AuthConfigDir, "hex.config"), + filelib:ensure_dir(AuthConfigFile), + AuthConfig = #{<<"$oauth">> => #{ + access_token => OAuthToken, + expires_at => erlang:system_time(second) + 3600 + }}, + ok = file:write_file(AuthConfigFile, io_lib:format("~p.~n", [AuthConfig])), + + meck:expect(rebar_dir, global_config_dir, fun(_) -> AuthConfigDir end), + + %% For OAuth test: verify Bearer token is passed and return success + meck:expect(r3_hex_repo, get_tarball, + fun(#{repo_key := <<"Bearer ", _/binary>>}, _, _) -> + {ok, {200, #{<<"etag">> => ?good_etag}, PkgContents}}; + (_, _, _) -> + {ok, {401, #{}, #{<<"message">> => <<"Unauthorized">>}}} + end), + + [{cache_root, CacheRoot}, + {cache_dir, CacheDir}, + {tmp_dir, TmpDir}, + {mock_table, Tid}, + {state, rebar_state:new()} | Config]. + +%% Mock config for OAuth refresh tests - returns 401 first, then success after refresh +mock_config_oauth_refresh(Name, Config) -> + Priv = ?config(priv_dir, Config), + CacheRoot = filename:join([Priv, "cache", atom_to_list(Name)]), + TmpDir = filename:join([Priv, "tmp", atom_to_list(Name)]), + Tid = ets:new(registry_table, [public]), + AllDeps = [ + {{<<"goodpkg">>,<<"1.0.0">>}, [[], ?good_checksum, ?good_checksum, [<<"rebar3">>]]} + ], + ets:insert_new(Tid, AllDeps), + CacheDir = filename:join([CacheRoot, "hex", "com", "test", "packages"]), + filelib:ensure_dir(filename:join([CacheDir, "registry"])), + ok = ets:tab2file(Tid, filename:join([CacheDir, "registry"])), + + catch ets:delete(?PACKAGE_TABLE), + rebar_packages:new_package_table(), + lists:foreach(fun({{N, Vsn}, [Deps, InnerChecksum, OuterChecksum, _]}) -> + {ok, Parsed} = rebar_semver:parse_version(Vsn), + ets:insert(?PACKAGE_TABLE, #package{key={ec_cnv:to_binary(N), Parsed, <<"hexpm">>}, + dependencies=Deps, + retired=false, + inner_checksum=InnerChecksum, + outer_checksum=OuterChecksum}) + end, AllDeps), + + meck:new(r3_hex_repo, [passthrough]), + meck:expect(r3_hex_repo, get_package, + fun(_Config, PkgName) -> + Matches = ets:match_object(Tid, {{PkgName,'_'}, '_'}), + Releases = + [#{outer_checksum => OuterChecksum, + inner_checksum => InnerChecksum, + version => Vsn, + dependencies => Deps} || + {{_, Vsn}, [Deps, InnerChecksum, OuterChecksum, _]} <- Matches], + {ok, {200, #{}, Releases}} + end), + + meck:new(rebar_state, [passthrough]), + meck:expect(rebar_state, get, + fun(_State, rebar_packages_cdn, _Default) -> + "http://test.com/"; + (_, _, Default) -> + Default + end), + meck:expect(rebar_state, resources, + fun(_State) -> + DefaultConfig = r3_hex_core:default_config(), + %% trusted => true is required for OAuth token resolution + [rebar_resource_v2:new(pkg, rebar_pkg_resource, + #{repos => [DefaultConfig#{name => <<"hexpm">>, + trusted => true}], + base_config => #{}})] + end), + + meck:new(rebar_dir, [passthrough]), + meck:expect(rebar_dir, global_cache_dir, fun(_) -> CacheRoot end), + + meck:expect(rebar_packages, registry_dir, fun(_) -> {ok, CacheDir} end), + meck:expect(rebar_packages, package_dir, fun(_, _) -> {ok, CacheDir} end), + + meck:new(rebar_prv_update, [passthrough]), + meck:expect(rebar_prv_update, do, fun(State) -> {ok, State} end), + + {Pkg, Vsn} = ?config(pkg, Config), + PkgFile = <>, + {ok, PkgContents} = file:read_file(filename:join(?config(data_dir, Config), PkgFile)), + + OldToken = ?config(oauth_token, Config), + RefreshToken = ?config(oauth_refresh_token, Config), + NewToken = ?config(new_oauth_token, Config), + OldBearer = <<"Bearer ", OldToken/binary>>, + NewBearer = <<"Bearer ", NewToken/binary>>, + + %% Write auth config file with expired OAuth token and refresh token + AuthConfigDir = filename:join([CacheRoot, "config"]), + AuthConfigFile = filename:join(AuthConfigDir, "hex.config"), + filelib:ensure_dir(AuthConfigFile), + AuthConfig = #{<<"$oauth">> => #{ + access_token => OldToken, + refresh_token => RefreshToken, + expires_at => erlang:system_time(second) - 100 %% Expired + }}, + ok = file:write_file(AuthConfigFile, io_lib:format("~p.~n", [AuthConfig])), + + meck:expect(rebar_dir, global_config_dir, fun(_) -> AuthConfigDir end), + + %% Mock update_repo_auth_config to track calls but not actually write + meck:new(rebar_hex_repos, [passthrough]), + meck:expect(rebar_hex_repos, update_repo_auth_config, fun(_Updates, _RepoName, _State) -> ok end), + + %% Return 401 for old token, 200 for new token + meck:expect(r3_hex_repo, get_tarball, + fun(#{repo_key := Key}, _, _) when Key =:= OldBearer -> + {ok, {401, #{}, #{<<"message">> => <<"Unauthorized">>}}}; + (#{repo_key := Key}, _, _) when Key =:= NewBearer -> + {ok, {200, #{<<"etag">> => ?good_etag}, PkgContents}}; + (_, _, _) -> + {ok, {401, #{}, #{<<"message">> => <<"Unauthorized">>}}} + end), + + %% Mock OAuth refresh + meck:new(r3_hex_api_oauth, [passthrough]), + meck:expect(r3_hex_api_oauth, refresh_token, + fun(_Config, _ClientId, _RefreshToken) -> + {ok, {200, #{}, #{<<"access_token">> => NewToken, + <<"refresh_token">> => <<"new-refresh-token">>, + <<"expires_in">> => 3600}}} + end), + + [{cache_root, CacheRoot}, + {cache_dir, CacheDir}, + {tmp_dir, TmpDir}, + {mock_table, Tid}, + {state, rebar_state:new()} | Config]. diff --git a/apps/rebar/test/rebar_pkg_repos_SUITE.erl b/apps/rebar/test/rebar_pkg_repos_SUITE.erl index 4cb5ee26a..7113acc44 100644 --- a/apps/rebar/test/rebar_pkg_repos_SUITE.erl +++ b/apps/rebar/test/rebar_pkg_repos_SUITE.erl @@ -357,14 +357,14 @@ organization_merging(_Config) -> ?assertMatch({ok, #resource{state=#{repos := [#{name := <<"hexpm:repo-1">>, parent := <<"hexpm">>, - repo_name := <<"repo-1">>, + repo_name := <<"hexpm">>, api_repository := <<"repo-1">>, repo_organization := <<"repo-1">>, read_key := <<"read key">>, write_key := <<"write key hexpm">>}, #{name := <<"hexpm:repo-2">>, parent := <<"hexpm">>, - repo_name := <<"repo-2">>, + repo_name := <<"hexpm">>, api_repository := <<"repo-2">>, repo_organization := <<"repo-2">>, read_key := <<"read key 2">>, diff --git a/vendor_hex_core.sh b/vendor_hex_core.sh index 79eaa2c98..e596a68ea 100755 --- a/vendor_hex_core.sh +++ b/vendor_hex_core.sh @@ -39,6 +39,7 @@ filenames="hex_core.hrl \ hex_api_package_owner.erl \ hex_api_release.erl \ hex_api_user.erl \ + hex_cli_auth.erl \ hex_licenses.erl \ hex_safe_binary_to_term.erl \ safe_erl_term.xrl" @@ -56,6 +57,7 @@ search_to_replace="hex_core: \ hex_http \ hex_repo \ hex_api \ + hex_cli_auth \ hex_licenses \ hex_safe_binary_to_term \ safe_erl_term"