From 12c73b2fd9d9df901a9200ce2ac13eb447e47474 Mon Sep 17 00:00:00 2001 From: Stewart Smith Date: Wed, 5 Jun 2024 23:38:37 +0000 Subject: [PATCH] Add JSON output to advisory list Similar to the repolist --json output, do it for listing advisories that apply to the system ("dnf advisory list") as well as for the references currently supported Bugzilla and CVE types of references. These follow what is output on the terminal. The exception is that the queries with a reference (--with-bz and --with-cve) also print the advisory name. This is just to make it a bit easier for other tools which may already know about that advisory ID. Example of advisory list --json: [ { "name":"FEDORA-2024-143f0443e1", "type":"unspecified", "severity":"None", "nevra":"yum-4.19.2-1.fc40.noarch", "buildtime":"2024-04-19 21:20:20" }, { "name":"FEDORA-2024-1e8a70c30e", "type":"enhancement", "severity":"None", "nevra":"zstd-1.5.6-1.fc40.aarch64", "buildtime":"2024-04-19 21:20:20" } ] Example of advisory list --with-bz --json: [ { "advisory_name":"FEDORA-2024-0c9d3b51d4", "advisory_type":"security", "advisory_severity":"security", "advisory_buildtime":"2024-05-02 01:55:56", "nevra":"tpm2-tss-fapi-4.1.0-1.fc40.aarch64", "references":[ { "reference_id":"2271763", "reference_type":"bugzilla", }, { "reference_id":"2277437", "reference_type":"bugzilla", } ] } ] --- dnf5/commands/advisory/advisory_list.cpp | 19 ++++- dnf5/commands/advisory/advisory_list.hpp | 3 + include/libdnf5-cli/output/advisorylist.hpp | 9 ++ libdnf5-cli/output/advisorylist.cpp | 91 +++++++++++++++++++-- 4 files changed, 112 insertions(+), 10 deletions(-) diff --git a/dnf5/commands/advisory/advisory_list.cpp b/dnf5/commands/advisory/advisory_list.cpp index 7ec507fe0..5eaf1cc91 100644 --- a/dnf5/commands/advisory/advisory_list.cpp +++ b/dnf5/commands/advisory/advisory_list.cpp @@ -66,10 +66,19 @@ void AdvisoryListCommand::process_and_print_queries( std::vector> cli_installed_pkgs; std::vector> cli_not_installed_pkgs; + std::string reference_type; if (with_bz->get_value()) { - libdnf5::cli::output::print_advisorylist_references_table(not_installed_pkgs, installed_pkgs, "bugzilla"); + reference_type = "bugzilla"; } else if (with_cve->get_value()) { - libdnf5::cli::output::print_advisorylist_references_table(not_installed_pkgs, installed_pkgs, "cve"); + reference_type = "cve"; + } + if (with_bz->get_value() || with_cve->get_value()) { + if (ctx.get_json_output_requested()) + libdnf5::cli::output::print_advisorylist_references_json( + not_installed_pkgs, installed_pkgs, reference_type); + else + libdnf5::cli::output::print_advisorylist_references_table( + not_installed_pkgs, installed_pkgs, reference_type); } else { cli_installed_pkgs.reserve(installed_pkgs.size()); for (const auto & obj : installed_pkgs) { @@ -79,7 +88,11 @@ void AdvisoryListCommand::process_and_print_queries( for (const auto & obj : not_installed_pkgs) { cli_not_installed_pkgs.emplace_back(new output::AdvisoryPackageAdapter(obj)); } - libdnf5::cli::output::print_advisorylist_table(cli_not_installed_pkgs, cli_installed_pkgs); + if (ctx.get_json_output_requested()) { + libdnf5::cli::output::print_advisorylist_json(cli_not_installed_pkgs, cli_installed_pkgs); + } else { + libdnf5::cli::output::print_advisorylist_table(cli_not_installed_pkgs, cli_installed_pkgs); + } } } diff --git a/dnf5/commands/advisory/advisory_list.hpp b/dnf5/commands/advisory/advisory_list.hpp index 20733cac3..3c63a6047 100644 --- a/dnf5/commands/advisory/advisory_list.hpp +++ b/dnf5/commands/advisory/advisory_list.hpp @@ -22,6 +22,8 @@ along with libdnf. If not, see . #include "advisory_subcommand.hpp" +#include + namespace dnf5 { class AdvisoryListCommand : public AdvisorySubCommand { @@ -31,6 +33,7 @@ class AdvisoryListCommand : public AdvisorySubCommand { void set_argument_parser() override { AdvisorySubCommand::set_argument_parser(); get_argument_parser_command()->set_description(_("List advisories")); + create_json_option(*this); } protected: diff --git a/include/libdnf5-cli/output/advisorylist.hpp b/include/libdnf5-cli/output/advisorylist.hpp index b00b306e3..e96ddf432 100644 --- a/include/libdnf5-cli/output/advisorylist.hpp +++ b/include/libdnf5-cli/output/advisorylist.hpp @@ -34,11 +34,20 @@ LIBDNF_CLI_API void print_advisorylist_table( std::vector> & advisory_package_list_not_installed, std::vector> & advisory_package_list_installed); +LIBDNF_CLI_API void print_advisorylist_json( + std::vector> & advisory_package_list_not_installed, + std::vector> & advisory_package_list_installed); + LIBDNF_CLI_API void print_advisorylist_references_table( std::vector & advisory_package_list_not_installed, std::vector & advisory_package_list_installed, std::string reference_type); +LIBDNF_CLI_API void print_advisorylist_references_json( + std::vector & advisory_package_list_not_installed, + std::vector & advisory_package_list_installed, + std::string reference_type); + } // namespace libdnf5::cli::output #endif // LIBDNF5_CLI_OUTPUT_ADVISORYLIST_HPP diff --git a/libdnf5-cli/output/advisorylist.cpp b/libdnf5-cli/output/advisorylist.cpp index 2501ba143..1031f14bd 100644 --- a/libdnf5-cli/output/advisorylist.cpp +++ b/libdnf5-cli/output/advisorylist.cpp @@ -21,9 +21,12 @@ along with libdnf. If not, see . #include "utils/string.hpp" +#include #include #include +#include + namespace libdnf5::cli::output { namespace { @@ -72,6 +75,14 @@ void add_line_into_advisorylist_table( } // namespace +static std::string get_reference_type_pretty_name(std::string type) { + if (type == "bugzilla") { + return "Bugzilla"; + } else if (type == "cve") { + return "CVE"; + } + return type; +} void print_advisorylist_table( std::vector> & advisory_package_list_not_installed, @@ -104,18 +115,65 @@ void print_advisorylist_table( scols_unref_table(table); } +static json_object * adv_pkg_as_json(auto & adv_pkg) { + auto advisory = adv_pkg->get_advisory(); + json_object * json_advisory = json_object_new_object(); + json_object_object_add(json_advisory, "name", json_object_new_string(advisory->get_name().c_str())); + json_object_object_add(json_advisory, "type", json_object_new_string(advisory->get_type().c_str())); + json_object_object_add(json_advisory, "severity", json_object_new_string(advisory->get_severity().c_str())); + json_object_object_add(json_advisory, "nevra", json_object_new_string(adv_pkg->get_nevra().c_str())); + unsigned long long buildtime = advisory->get_buildtime(); + json_object_object_add( + json_advisory, "buildtime", json_object_new_string(libdnf5::utils::string::format_epoch(buildtime).c_str())); + return json_advisory; +} + +static json_object * adv_refs_as_json(auto & reference_type, auto & adv_pkg, auto & advisory) { + json_object * json_advisory = json_object_new_object(); + json_object_object_add(json_advisory, "advisory_name", json_object_new_string(advisory.get_name().c_str())); + json_object_object_add(json_advisory, "advisory_type", json_object_new_string(advisory.get_type().c_str())); + json_object_object_add(json_advisory, "advisory_severity", json_object_new_string(advisory.get_type().c_str())); + unsigned long long buildtime = advisory.get_buildtime(); + json_object_object_add( + json_advisory, + "advisory_buildtime", + json_object_new_string(libdnf5::utils::string::format_epoch(buildtime).c_str())); + json_object_object_add(json_advisory, "nevra", json_object_new_string(adv_pkg.get_nevra().c_str())); + + json_object * json_adv_references = json_object_new_array(); + auto references = advisory.get_references({reference_type}); + for (auto reference : references) { + json_object * json_reference = json_object_new_object(); + json_object_object_add(json_reference, "reference_id", json_object_new_string(reference.get_id().c_str())); + json_object_object_add(json_reference, "reference_type", json_object_new_string(reference_type.c_str())); + json_object_array_add(json_adv_references, json_reference); + } + json_object_object_add(json_advisory, "references", json_adv_references); + return json_advisory; +} + + +void print_advisorylist_json( + std::vector> & advisory_package_list_not_installed, + std::vector> & advisory_package_list_installed) { + json_object * json_advisorylist = json_object_new_array(); + + for (auto & adv_pkg : advisory_package_list_not_installed) { + json_object_array_add(json_advisorylist, adv_pkg_as_json(adv_pkg)); + } + for (auto & adv_pkg : advisory_package_list_installed) { + json_object_array_add(json_advisorylist, adv_pkg_as_json(adv_pkg)); + } + std::cout << json_object_to_json_string_ext(json_advisorylist, JSON_C_TO_STRING_PRETTY) << std::endl; + json_object_put(json_advisorylist); +} + void print_advisorylist_references_table( std::vector & advisory_package_list_not_installed, std::vector & advisory_package_list_installed, std::string reference_type) { - std::string first_column_name; - if (reference_type == "cve") { - first_column_name = "CVE"; - } else { - first_column_name = "Bugzilla"; - } - struct libscols_table * table = create_advisorylist_table(first_column_name); + struct libscols_table * table = create_advisorylist_table(get_reference_type_pretty_name(reference_type)); for (auto adv_pkg : advisory_package_list_not_installed) { auto advisory = adv_pkg.get_advisory(); auto references = advisory.get_references({reference_type}); @@ -149,4 +207,23 @@ void print_advisorylist_references_table( scols_unref_table(table); } +void print_advisorylist_references_json( + std::vector & advisory_package_list_not_installed, + std::vector & advisory_package_list_installed, + std::string reference_type) { + json_object * json_advisory_refs = json_object_new_array(); + for (auto adv_pkg : advisory_package_list_not_installed) { + auto advisory = adv_pkg.get_advisory(); + auto json_refs = adv_refs_as_json(reference_type, adv_pkg, advisory); + json_object_array_add(json_advisory_refs, json_refs); + } + for (auto adv_pkg : advisory_package_list_installed) { + auto advisory = adv_pkg.get_advisory(); + auto json_refs = adv_refs_as_json(reference_type, adv_pkg, advisory); + json_object_array_add(json_advisory_refs, json_refs); + } + std::cout << json_object_to_json_string_ext(json_advisory_refs, JSON_C_TO_STRING_PRETTY) << std::endl; + json_object_put(json_advisory_refs); +} + } // namespace libdnf5::cli::output