diff --git a/src/hex_core.erl b/src/hex_core.erl index 8412cf2..e0897c9 100644 --- a/src/hex_core.erl +++ b/src/hex_core.erl @@ -46,6 +46,18 @@ %% %% * `repo_verify_origin' - If `true' will verify the repository signature origin, %% requires protobuf messages as of hex_core v0.4.0 (default: `true'). +%% +%% * `tarball_max_size' - Maximum size of package tarball, defaults to +%% `8_388_608' (8 MiB). Set to `undefined' to not enforce the limit. +%% +%% * `tarball_max_uncompressed_size' - Maximum size of uncompressed package tarball, defaults to +%% `67_108_864' (64 MiB). Set to `undefined' to not enforce the limit. +%% +%% * `docs_tarball_max_size' - Maximum size of docs tarball, defaults to +%% `16_777_216' (16 MiB). Set to `undefined' 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 `undefined' to not enforce the limit. -module(hex_core). -export([default_config/0]). @@ -81,10 +93,10 @@ repo_organization => binary() | undefined, repo_verify => boolean(), repo_verify_origin => boolean(), - tarball_max_size => pos_integer(), - tarball_max_uncompressed_size => pos_integer(), - docs_tarball_max_size => pos_integer(), - docs_tarball_max_uncompressed_size => pos_integer() + tarball_max_size => pos_integer() | undefined, + tarball_max_uncompressed_size => pos_integer() | undefined, + docs_tarball_max_size => pos_integer() | undefined, + docs_tarball_max_uncompressed_size => pos_integer() | undefined }. -spec default_config() -> config(). diff --git a/src/hex_tarball.erl b/src/hex_tarball.erl index 9beda1e..c6bf8ce 100644 --- a/src/hex_tarball.erl +++ b/src/hex_tarball.erl @@ -58,6 +58,11 @@ }} | {error, term()}. create(Metadata, Files, Config) -> + #{ + tarball_max_size := TarballMaxSize, + tarball_max_uncompressed_size := TarballMaxUncompressedSize + } = Config, + MetadataBinary = encode_metadata(Metadata), ContentsTarball = create_memory_tarball(Files), ContentsTarballCompressed = gzip(ContentsTarball), @@ -73,20 +78,23 @@ create(Metadata, Files, Config) -> {"contents.tar.gz", ContentsTarballCompressed} ], - Tarball = create_memory_tarball(OuterFiles), - OuterChecksum = checksum(Tarball), - - UncompressedSize = byte_size(ContentsTarball), - - case {(byte_size(Tarball) > TarballMaxSize), (UncompressedSize > TarballMaxUncompressedSize)} of - {_, true} -> - {error, {tarball, {too_big_uncompressed, TarballMaxUncompressedSize}}}; - {true, _} -> - {error, {tarball, {too_big_compressed, TarballMaxSize}}}; - {false, false} -> - {ok, #{ - tarball => Tarball, outer_checksum => OuterChecksum, inner_checksum => InnerChecksum - }} + case valid_size(ContentsTarball, TarballMaxUncompressedSize) of + true -> + 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_uncompressed, TarballMaxUncompressedSize}}} end. -spec create(metadata(), files()) -> @@ -111,22 +119,26 @@ create(Metadata, Files) -> %% ''' %% @end -spec create_docs(files(), hex_core:config()) -> {ok, tarball()} | {error, term()}. -create_docs(Files, #{ - docs_tarball_max_size := TarballMaxSize, - docs_tarball_max_uncompressed_size := TarballMaxUncompressedSize -}) -> +create_docs(Files, Config) -> + #{ + docs_tarball_max_size := TarballMaxSize, + docs_tarball_max_uncompressed_size := TarballMaxUncompressedSize + } = Config, + UncompressedTarball = create_memory_tarball(Files), - UncompressedSize = byte_size(UncompressedTarball), - Tarball = gzip(UncompressedTarball), - Size = byte_size(Tarball), - - case {(Size > TarballMaxSize), (UncompressedSize > TarballMaxUncompressedSize)} of - {_, true} -> - {error, {tarball, {too_big_uncompressed, TarballMaxUncompressedSize}}}; - {true, _} -> - {error, {tarball, {too_big_compressed, TarballMaxSize}}}; - {false, false} -> - {ok, Tarball} + + case valid_size(UncompressedTarball, TarballMaxUncompressedSize) of + true -> + 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_uncompressed, TarballMaxUncompressedSize}}} end. -spec create_docs(files()) -> {ok, tarball()}. @@ -167,19 +179,20 @@ create_docs(Files) -> metadata => metadata() }} | {error, term()}. -unpack(Tarball, _, #{tarball_max_size := TarballMaxSize}) when - byte_size(Tarball) > TarballMaxSize --> - {error, {tarball, too_big}}; -unpack(Tarball, Output, _Config) -> - case hex_erl_tar:extract({binary, Tarball}, [memory]) of - {ok, []} -> - {error, {tarball, empty}}; - {ok, FileList} -> - OuterChecksum = crypto:hash(sha256, Tarball), - do_unpack(maps:from_list(FileList), OuterChecksum, Output); - {error, Reason} -> - {error, {tarball, Reason}} +unpack(Tarball, Output, Config) -> + case valid_size(Tarball, maps:get(tarball_max_size, Config)) of + true -> + case hex_erl_tar:extract({binary, Tarball}, [memory]) of + {ok, []} -> + {error, {tarball, empty}}; + {ok, FileList} -> + OuterChecksum = crypto:hash(sha256, Tarball), + do_unpack(maps:from_list(FileList), OuterChecksum, Output); + {error, Reason} -> + {error, {tarball, Reason}} + end; + false -> + {error, {tarball, too_big}} end. %% @doc @@ -220,12 +233,13 @@ unpack(Tarball, Output) -> -spec unpack_docs (tarball(), memory, hex_core:config()) -> {ok, contents()} | {error, term()}; (tarball(), filename(), hex_core:config()) -> ok | {error, term()}. -unpack_docs(Tarball, _, #{docs_tarball_max_size := TarballMaxSize}) when - byte_size(Tarball) > TarballMaxSize --> - {error, {tarball, too_big}}; -unpack_docs(Tarball, Output, _Config) -> - unpack_tarball(Tarball, Output). +unpack_docs(Tarball, Output, Config) -> + case valid_size(Tarball, maps:get(docs_tarball_max_size, Config)) of + true -> + unpack_tarball(Tarball, Output); + false -> + {error, {tarball, too_big}} + end. -spec unpack_docs (tarball(), memory) -> {ok, contents()} | {error, term()}; @@ -602,6 +616,12 @@ gzip_no_header(Uncompressed) -> %% Helpers %%==================================================================== +%% @private +valid_size(Binary, undefined) when is_binary(Binary) -> + true; +valid_size(Binary, Limit) when is_binary(Binary) -> + byte_size(Binary) =< Limit. + %% @private binarify(Binary) when is_binary(Binary) -> Binary; binarify(Number) when is_number(Number) -> Number;