-module(go_over). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/go_over.gleam"). -export([warnings_for_json_results/1, print_json_warnings/1, skipped_workspace_warnings/1, print_warnings/3, audit_project/2, main/0]). -export_type([audit_result/0]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. ?MODULEDOC(false). -type audit_result() :: {audit_result, binary(), list(go_over@warning:warning()), list(go_over@warning:warning()), boolean(), go_over@config:format()}. -file("src/go_over.gleam", 35). ?DOC(false). -spec prefix_label(gleam@option:option(binary()), binary()) -> binary(). prefix_label(Prefix, Label) -> case Prefix of {some, Path} -> <<<<<<"["/utf8, Path/binary>>/binary, "] "/utf8>>/binary, Label/binary>>; none -> Label end. -file("src/go_over.gleam", 42). ?DOC(false). -spec print_warnings_output( list(go_over@warning:warning()), go_over@config:config(), binary(), gleam@option:option(binary()) ) -> nil. print_warnings_output(Vulns, Conf, Label, Prefix) -> Label@1 = prefix_label(Prefix, Label), gleam_stdlib:print_error(Label@1), case erlang:element(6, Conf) of minimal -> _pipe = Vulns, _pipe@1 = gleam@list:map( _pipe, fun go_over@warning:format_as_string_minimal/1 ), _pipe@2 = gleam@string:join(_pipe@1, <<""/utf8>>), gleam_stdlib:print_error(_pipe@2); j_s_o_n -> nil; s_a_r_i_f -> nil; _ -> _pipe@3 = Vulns, _pipe@4 = gleam@list:map( _pipe@3, fun go_over@warning:format_as_string/1 ), _pipe@5 = gleam@string:join( _pipe@4, <<"\n-----------------------------------------------\n"/utf8>> ), gleam_stdlib:print_error(_pipe@5) end. -file("src/go_over.gleam", 70). ?DOC(false). -spec warnings_label(list(go_over@warning:warning()), binary()) -> binary(). warnings_label(Vulns, Kind) -> <<<<<<<<<<"⛔ "/utf8, (erlang:integer_to_binary(erlang:length(Vulns)))/binary>>/binary, " "/utf8>>/binary, Kind/binary>>/binary, "(s) FOUND!"/utf8>>/binary, (<<"\n-----------------------------------------------\n"/utf8>>)/binary>>. -file("src/go_over.gleam", 79). ?DOC(false). -spec info_label(list(go_over@warning:warning())) -> binary(). info_label(Vulns) -> <<<<<<"ℹ️ "/utf8, (erlang:integer_to_binary(erlang:length(Vulns)))/binary>>/binary, " Item(s) of Note"/utf8>>/binary, (<<"\n-----------------------------------------------\n"/utf8>>)/binary>>. -file("src/go_over.gleam", 86). ?DOC(false). -spec warnings_for_json(audit_result()) -> list(go_over@warning:warning()). warnings_for_json(Result) -> lists:append(erlang:element(4, Result), erlang:element(3, Result)). -file("src/go_over.gleam", 90). ?DOC(false). -spec warnings_for_json_results(list(audit_result())) -> list(go_over@warning:warning()). warnings_for_json_results(Results) -> gleam@list:flat_map(Results, fun warnings_for_json/1). -file("src/go_over.gleam", 94). ?DOC(false). -spec print_json_warnings(list(go_over@warning:warning())) -> nil. print_json_warnings(Warnings) -> _pipe = Warnings, _pipe@1 = gleam@list:map(_pipe, fun go_over@warning:format_as_json/1), _pipe@2 = gleam@json:preprocessed_array(_pipe@1), _pipe@3 = gleam@json:to_string(_pipe@2), gleam_stdlib:print_error(_pipe@3). -file("src/go_over.gleam", 102). ?DOC(false). -spec write_sarif(list(audit_result()), gleam@option:option(binary())) -> nil. write_sarif(Results, Output) -> Runs = gleam@list:map( Results, fun(Result) -> {erlang:element(2, Result), warnings_for_json(Result)} end ), Content = begin _pipe = go_over@sarif:to_sarif_log(Runs), gleam@json:to_string(_pipe) end, case Output of none -> _pipe@1 = Content, gleam_stdlib:println(_pipe@1); {some, Path} -> case begin _pipe@2 = Path, simplifile:write(_pipe@2, Content) end of {ok, nil} -> nil; {error, _} -> gleam_stdlib:println_error( <<"could not write SARIF output to "/utf8, Path/binary>> ), shellout_ffi:os_exit(1) end end. -file("src/go_over.gleam", 128). ?DOC(false). -spec skipped_workspace_warnings(list(binary())) -> list(go_over@warning:warning()). skipped_workspace_warnings(Skipped) -> gleam@list:map( Skipped, fun(Path) -> go_over@warning:info_to_warning( Path, <<<<"Info: project at '"/utf8, Path/binary>>/binary, "' was skipped (exceeds workspace_max_depth)"/utf8>> ) end ). -file("src/go_over.gleam", 139). ?DOC(false). -spec print_warnings( list(go_over@warning:warning()), go_over@config:config(), gleam@option:option(binary()) ) -> nil. print_warnings(Vulns, Conf, Prefix) -> print_warnings_output( Vulns, Conf, warnings_label(Vulns, <<"WARNING"/utf8>>), Prefix ), shellout_ffi:os_exit(1). -file("src/go_over.gleam", 148). ?DOC(false). -spec read_project_config(binary()) -> go_over@config:config(). read_project_config(Project_root) -> Gleam_toml = filepath:join(Project_root, <<"gleam.toml"/utf8>>), case simplifile:read(Gleam_toml) of {ok, _} -> go_over@config:read_config(Gleam_toml); {error, _} -> go_over@config:default_config() end. -file("src/go_over.gleam", 435). ?DOC(false). -spec run_deps_outdated(binary()) -> boolean(). run_deps_outdated(Project_root) -> go_over@util@print:high( <<"The --outdated flag is deprecated. Use `gleam deps outdated` instead."/utf8>> ), case shellout:command( <<"gleam"/utf8>>, [<<"deps"/utf8>>, <<"outdated"/utf8>>], Project_root, [let_be_stdout] ) of {ok, _} -> false; {error, _} -> true end. -file("src/go_over.gleam", 157). ?DOC(false). -spec audit_project(go_over@config:flags(), binary()) -> {ok, audit_result()} | {error, binary()}. audit_project(Flags, Project_root) -> Manifest_toml = filepath:join(Project_root, <<"manifest.toml"/utf8>>), Project_config = read_project_config(Project_root), gleam@result:'try'( go_over@config:merge_flags_and_config(Flags, Project_config), fun(Conf) -> go_over@util@globals:set_project_root(Project_root), go_over@util@globals:set_verbose(erlang:element(7, Conf)), go_over@util@globals:set_use_global_cache(erlang:element(8, Conf)), go_over@util@globals:set_force(erlang:element(5, Conf)), Machine_output = (erlang:element(6, Conf) =:= s_a_r_i_f) orelse (erlang:element( 6, Conf ) =:= j_s_o_n), Spinner = case Machine_output of true -> none; false -> go_over@util@spinner:new_spinner(<<"Let's do this!"/utf8>>) end, gxyz@function:ignore_result( erlang:element(5, Conf), gxyz@function:freeze1( fun simplifile_erl:delete/1, go_over@util@globals:go_over_path() ) ), go_over@util@spinner:set_text_spinner( Spinner, <<"Reading manifest"/utf8>> ), Manifest_pkgs = go_over@packages:read_manifest(Manifest_toml), Pkgs_audited = begin _pipe = Manifest_pkgs, _pipe@1 = go_over@config:filter_dev_dependencies(Conf, _pipe), go_over@config:filter_indirect(Conf, _pipe@1) end, Hex_pkgs = gleam@list:filter( Pkgs_audited, fun(P) -> erlang:element(6, P) =:= package_source_hex end ), go_over@util@spinner:set_text_spinner( Spinner, <<"Checking packages: "/utf8, (go_over@util@print:raw( <<"vulnerable"/utf8>>, <<"red"/utf8>> ))/binary>> ), All_advisories = go_over@advisories@advisories:fetch_all(), Vulnerable_warnings = go_over@sources:get_vulnerable_warnings( Pkgs_audited, Conf, All_advisories ), go_over@util@spinner:set_text_spinner( Spinner, <<"Checking packages: "/utf8, (go_over@util@print:raw( <<"retired"/utf8>>, <<"yellow"/utf8>> ))/binary>> ), Retired_warnings = go_over@sources:get_retired_warnings( Hex_pkgs, Conf ), {Hex_warnings, Dependency_licenses} = case erlang:element(10, Conf) of [] -> {[], []}; _ -> go_over@util@spinner:set_text_spinner( Spinner, <<"Checking packages: "/utf8, (go_over@util@print:raw( <<"licenses"/utf8>>, <<"brightmagenta"/utf8>> ))/binary>> ), go_over@sources:get_hex_warnings(Hex_pkgs, Conf) end, go_over@util@spinner:set_text_spinner( Spinner, <<"Filtering warnings"/utf8>> ), Audit_warnings = begin _pipe@2 = lists:append(Retired_warnings, Vulnerable_warnings), lists:append(_pipe@2, Hex_warnings) end, Unnecessary_warnings = go_over@config:unnecessary_ignore_warnings( Conf, Manifest_pkgs, Audit_warnings, Dependency_licenses, All_advisories ), Fatal_warnings = begin _pipe@3 = Audit_warnings, _pipe@4 = go_over@config:filter_package_warnings(Conf, _pipe@3), go_over@config:filter_severity(Conf, _pipe@4) end, go_over@util@spinner:stop_spinner(Spinner), Outdated_failed = case erlang:element(3, Conf) of false -> false; true -> run_deps_outdated(Project_root) end, Info_warnings = lists:append( Unnecessary_warnings, go_over@warning:git_deps_to_warnings(Manifest_pkgs) ), {ok, {audit_result, Project_root, Fatal_warnings, Info_warnings, Outdated_failed, erlang:element(6, Conf)}} end ). -file("src/go_over.gleam", 266). ?DOC(false). -spec print_no_issues_success(integer()) -> nil. print_no_issues_success(Info_count) -> case Info_count of 0 -> go_over@util@print:success(<<"✅ No warnings found!"/utf8>>); _ -> go_over@util@print:success( <<<<"✅ No security issues found ("/utf8, (erlang:integer_to_binary(Info_count))/binary>>/binary, " item(s) of note)"/utf8>> ) end. -file("src/go_over.gleam", 278). ?DOC(false). -spec run(go_over@config:flags()) -> nil. run(Flags) -> Workspace_mode = gleam@option:is_some(erlang:element(10, Flags)), {Results, Workspace_skipped} = case erlang:element(10, Flags) of {some, Scan_root} -> Scan_config = read_project_config(Scan_root), Max_depth = erlang:element(15, Scan_config), case go_over@workspace:discover_or_error(Scan_root, Max_depth) of {ok, {discover_result, Projects, Skipped}} -> {gleam@list:map( Projects, fun(Project_root) -> case audit_project(Flags, Project_root) of {ok, Result} -> Result; {error, E} -> gleam_stdlib:println_error(E), shellout_ffi:os_exit(1), go_over@util@util:do_panic() end end ), Skipped}; {error, E@1} -> gleam_stdlib:println_error(E@1), shellout_ffi:os_exit(1), go_over@util@util:do_panic() end; none -> Project_root@1 = gleam@option:unwrap( erlang:element(9, Flags), <<"."/utf8>> ), Result@2 = case audit_project(Flags, Project_root@1) of {ok, Result@1} -> Result@1; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"go_over"/utf8>>, function => <<"run"/utf8>>, line => 309, value => _assert_fail, start => 7678, 'end' => 7736, pattern_start => 7689, pattern_end => 7699}) end, {[Result@2], []} end, Skipped_warnings = skipped_workspace_warnings(Workspace_skipped), Results@1 = case Skipped_warnings of [] -> Results; _ -> case Results of [First | Rest] -> [{audit_result, erlang:element(2, First), erlang:element(3, First), lists:append( Skipped_warnings, erlang:element(4, First) ), erlang:element(5, First), erlang:element(6, First)} | Rest]; [] -> Results end end, case {Workspace_mode, erlang:element(7, Flags)} of {true, none} -> case go_over@config:validate_workspace_formats( gleam@list:map( Results@1, fun(R) -> {erlang:element(6, R), erlang:element(2, R)} end ) ) of {error, E@2} -> gleam_stdlib:println_error(E@2), shellout_ffi:os_exit(1), go_over@util@util:do_panic(); {ok, nil} -> nil end; {_, _} -> nil end, Prefix_for = fun(Result@3) -> case Workspace_mode of true -> {some, erlang:element(2, Result@3)}; false -> none end end, Display_conf = fun(Result@4) -> _record = go_over@config:default_config(), {config, erlang:element(2, _record), erlang:element(3, _record), erlang:element(4, _record), erlang:element(5, _record), erlang:element(6, Result@4), erlang:element(7, _record), erlang:element(8, _record), erlang:element(9, _record), erlang:element(10, _record), erlang:element(11, _record), erlang:element(12, _record), erlang:element(13, _record), erlang:element(14, _record), erlang:element(15, _record), erlang:element(16, _record), erlang:element(17, _record)} end, Output_format = case Results@1 of [] -> minimal; [First@1 | _] -> erlang:element(6, First@1) end, Sarif_output = Output_format =:= s_a_r_i_f, Json_output = Output_format =:= j_s_o_n, case {erlang:element(11, Flags), Sarif_output} of {{some, _}, false} -> gleam_stdlib:println_error( <<"--sarif-output requires --format sarif"/utf8>> ), shellout_ffi:os_exit(1); {_, _} -> nil end, case {Sarif_output, Json_output} of {false, false} -> gleam@list:each( Results@1, fun(Result@5) -> case erlang:element(4, Result@5) of [] -> nil; Info -> print_warnings_output( Info, Display_conf(Result@5), info_label(Info), Prefix_for(Result@5) ) end end ); {false, true} -> print_json_warnings(warnings_for_json_results(Results@1)); {true, _} -> nil end, Any_fatal = gleam@list:any( Results@1, fun(R@1) -> not gleam@list:is_empty(erlang:element(3, R@1)) end ), Any_outdated_failed = gleam@list:any( Results@1, fun(R@2) -> erlang:element(5, R@2) end ), Info_count = begin _pipe = gleam@list:flat_map( Results@1, fun(R@3) -> erlang:element(4, R@3) end ), erlang:length(_pipe) end, case {Any_fatal, Any_outdated_failed, Sarif_output, Json_output} of {false, false, true, _} -> write_sarif(Results@1, erlang:element(11, Flags)), nil; {false, false, false, _} -> print_no_issues_success(Info_count), nil; {false, true, true, _} -> write_sarif(Results@1, erlang:element(11, Flags)), shellout_ffi:os_exit(1); {false, true, false, _} -> shellout_ffi:os_exit(1); {true, _, true, _} -> write_sarif(Results@1, erlang:element(11, Flags)), shellout_ffi:os_exit(1); {true, _, false, true} -> shellout_ffi:os_exit(1); {true, _, false, false} -> gleam@list:each( Results@1, fun(Result@6) -> case erlang:element(3, Result@6) of [] -> nil; Vulns -> print_warnings_output( Vulns, Display_conf(Result@6), warnings_label(Vulns, <<"WARNING"/utf8>>), Prefix_for(Result@6) ) end end ), shellout_ffi:os_exit(1) end. -file("src/go_over.gleam", 259). ?DOC(false). -spec main() -> nil. main() -> case go_over@config:parse_flags(shellout_ffi:start_arguments()) of {error, Message} -> gleam_stdlib:println(Message); {ok, Flags} -> run(Flags) end.