From 71ebf13c67394c640c4418ca0a1a81bfec5df26b Mon Sep 17 00:00:00 2001 From: Vadim Kononov Date: Thu, 1 Feb 2024 16:49:58 -0600 Subject: [PATCH 01/14] Everything is working but needs refactoring, tests, linting, and RBS echecking. --- README.md | 7 ++ lib/package/audit/cli.rb | 15 ++-- lib/package/audit/enum/format.rb | 14 ++++ lib/package/audit/enum/option.rb | 4 +- lib/package/audit/enum/technology.rb | 2 +- lib/package/audit/models/package.rb | 2 +- lib/package/audit/services/command_parser.rb | 19 +++-- lib/package/audit/services/package_printer.rb | 75 ++++++++++++++----- sig/package/audit/enum/option.rbs | 4 +- 9 files changed, 105 insertions(+), 37 deletions(-) create mode 100644 lib/package/audit/enum/format.rb diff --git a/README.md b/README.md index 89400aa..a449165 100644 --- a/README.md +++ b/README.md @@ -219,3 +219,10 @@ The gem is available as open source under the terms of the [MIT License](https:/ ## Code of Conduct Everyone interacting in the Package::Audit project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/tactica/package-audit/blob/main/CODE_OF_CONDUCT.md). + + + +| Package | Version | Latest | Latest Date | Groups | Vulnerabilities | Risk | Risk Explanation | +|--------------|---------|--------|-------------|-------------|-----------------|------|-----------------------------------------| +| rubocop-rake | 0.6.0 | 0.6.0 | 2021-06-28 | development | | low | no updates by author in over 2 years | +| rubocop | 1.60.1 | 1.60.2 | 2024-01-24 | development | | low | not at latest version | diff --git a/lib/package/audit/cli.rb b/lib/package/audit/cli.rb index 8642aa9..7747c43 100644 --- a/lib/package/audit/cli.rb +++ b/lib/package/audit/cli.rb @@ -1,5 +1,6 @@ require_relative 'const/file' require_relative 'const/time' +require_relative 'enum/format' require_relative 'enum/option' require_relative 'services/command_parser' require_relative 'util//risk_legend' @@ -25,12 +26,12 @@ class CLI < Thor class_option Enum::Option::INCLUDE_IGNORED, type: :boolean, default: false, desc: 'Include packages ignored by a configuration file' - class_option Enum::Option::CSV, + class_option Enum::Option::FORMAT, + aliases: '-f', banner: Enum::Format.all.join('|'), type: :string, + desc: "Output reports using a different format (e.g. CSV or Markdown)" + class_option Enum::Option::EXCLUDE_HEADERS, type: :boolean, default: false, - desc: 'Output reports using comma separated values (CSV)' - class_option Enum::Option::CSV_EXCLUDE_HEADERS, - type: :boolean, default: false, - desc: "Hide headers when using the --#{Enum::Option::CSV} option" + desc: "Hide headers when using the --#{Enum::Option::FORMAT} option" map '-v' => :version map '--version' => :version @@ -82,8 +83,8 @@ def respond_to_missing? def within_rescue_block yield - rescue StandardError => e - exit_with_error "#{e.class}: #{e.message}" + # rescue StandardError => e + # exit_with_error "#{e.class}: #{e.message}" end def exit_with_error(msg) diff --git a/lib/package/audit/enum/format.rb b/lib/package/audit/enum/format.rb new file mode 100644 index 0000000..df7a663 --- /dev/null +++ b/lib/package/audit/enum/format.rb @@ -0,0 +1,14 @@ +module Package + module Audit + module Enum + module Format + CSV = 'csv' + MD = 'md' + + def self.all + constants.map { |key| const_get(key) }.sort + end + end + end + end +end diff --git a/lib/package/audit/enum/option.rb b/lib/package/audit/enum/option.rb index bcd7741..6acfeb4 100644 --- a/lib/package/audit/enum/option.rb +++ b/lib/package/audit/enum/option.rb @@ -3,8 +3,8 @@ module Audit module Enum module Option CONFIG = 'config' - CSV = 'csv' - CSV_EXCLUDE_HEADERS = 'exclude-headers' + FORMAT = 'format' + EXCLUDE_HEADERS = 'exclude-headers' GROUP = 'group' INCLUDE_IGNORED = 'include-ignored' TECHNOLOGY = 'technology' diff --git a/lib/package/audit/enum/technology.rb b/lib/package/audit/enum/technology.rb index 24b0f90..8d2998f 100644 --- a/lib/package/audit/enum/technology.rb +++ b/lib/package/audit/enum/technology.rb @@ -6,7 +6,7 @@ module Technology RUBY = 'ruby' def self.all - constants.map { |key| Enum::Technology.const_get(key) }.sort + constants.map { |key| const_get(key) }.sort end end end diff --git a/lib/package/audit/models/package.rb b/lib/package/audit/models/package.rb index 10f68e3..d099589 100644 --- a/lib/package/audit/models/package.rb +++ b/lib/package/audit/models/package.rb @@ -41,7 +41,7 @@ def risk? end def group_list - @groups.join('|') + @groups.join(' ') end def vulnerabilities_grouped diff --git a/lib/package/audit/services/command_parser.rb b/lib/package/audit/services/command_parser.rb index 63c29fa..8dd2ccc 100644 --- a/lib/package/audit/services/command_parser.rb +++ b/lib/package/audit/services/command_parser.rb @@ -18,9 +18,10 @@ def initialize(dir, options, report) @dir = dir @options = options @report = report - @config = parse_config_file + @config = parse_config_file! @groups = @options[Enum::Option::GROUP] - @technologies = parse_technologies + @technologies = parse_technologies! + validate_format! @spinner = Util::Spinner.new('Evaluating packages and their dependencies...') end @@ -71,8 +72,8 @@ def process_technologies # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticCo def print_results(technology, pkgs, ignored_pkgs) PackagePrinter.new(@options, pkgs).print(Const::Fields::DEFAULT) - print_summary(technology, pkgs, ignored_pkgs) unless @options[Enum::Option::CSV] - print_disclaimer(technology) unless @options[Enum::Option::CSV] || pkgs.empty? + print_summary(technology, pkgs, ignored_pkgs) unless @options[Enum::Option::FORMAT] == Enum::Format::CSV + print_disclaimer(technology) unless @options[Enum::Option::FORMAT] == Enum::Format::CSV || pkgs.empty? end def print_summary(technology, pkgs, ignored_pkgs) @@ -103,7 +104,7 @@ def learn_more_command(technology) end end - def parse_config_file + def parse_config_file! if @options[Enum::Option::CONFIG].nil? YAML.load_file("#{@dir}/#{Const::File::CONFIG}") if File.exist? "#{@dir}/#{Const::File::CONFIG}" elsif File.exist? @options[Enum::Option::CONFIG] @@ -113,7 +114,13 @@ def parse_config_file end end - def parse_technologies + def validate_format! + format = @options[Enum::Option::FORMAT] + raise ArgumentError, "Invalid format: #{format}, should be one of [#{Enum::Format.all.join('|')}]" unless + @options[Enum::Option::FORMAT].nil? || Enum::Format.all.include?(format) + end + + def parse_technologies! technology_validator = Technology::Validator.new(@dir) @options[Enum::Option::TECHNOLOGY]&.each { |technology| technology_validator.validate! technology } @options[Enum::Option::TECHNOLOGY] || Technology::Detector.new(@dir).detect diff --git a/lib/package/audit/services/package_printer.rb b/lib/package/audit/services/package_printer.rb index 1744a76..c830c06 100644 --- a/lib/package/audit/services/package_printer.rb +++ b/lib/package/audit/services/package_printer.rb @@ -12,7 +12,7 @@ class PackagePrinter COLUMN_GAP = 2 - def initialize(options, pkgs) + def initialize( options, pkgs) @options = options @pkgs = pkgs end @@ -21,8 +21,11 @@ def print(fields) check_fields(fields) return if @pkgs.empty? - if @options[Enum::Option::CSV] - csv(fields, exclude_headers: @options[Enum::Option::CSV_EXCLUDE_HEADERS]) + case @options[Enum::Option::FORMAT] + when Enum::Format::CSV + csv(fields, exclude_headers: @options[Enum::Option::EXCLUDE_HEADERS]) + when Enum::Format::MD + markdown(fields) else pretty(fields) end @@ -69,21 +72,7 @@ def pretty(fields = Const::Fields::DEFAULT) # rubocop:disable Metrics/AbcSize, M @pkgs.each do |pkg| puts fields.map { |key| - val = pkg.send(key) || '' - val = case key - when :groups - pkg.group_list - when :risk_type - Formatter::Risk.new(pkg.risk_type).format - when :version - Formatter::Version.new(pkg.version, pkg.latest_version).format - when :vulnerabilities - Formatter::Vulnerability.new(pkg.vulnerabilities).format - when :latest_version_date - Formatter::VersionDate.new(pkg.latest_version_date).format - else - val - end + val = get_field_value(pkg, key) formatting_length = val.length - val.gsub(BASH_FORMATTING_REGEX, '').length val.ljust(instance_variable_get(:"@max_#{key}") + formatting_length) @@ -106,6 +95,56 @@ def csv(fields, exclude_headers: false) puts fields.join(',') unless exclude_headers @pkgs.map { |gem| puts gem.to_csv(value_fields) } end + + def markdown(fields) + # Calculate the maximum width for each column, including header titles and content + max_widths = fields.map do |field| + max_content_width = [@pkgs.map { |pkg| + value = get_field_value(pkg, field).to_s.gsub(BASH_FORMATTING_REGEX, '').length + value + }.max, Const::Fields::HEADERS[field].gsub(BASH_FORMATTING_REGEX, '').length].max + max_content_width + end + + # Construct the header with padding + header = fields.map.with_index { |field, index| + Const::Fields::HEADERS[field].gsub(BASH_FORMATTING_REGEX, '').ljust(max_widths[index]) + }.join(" | ") + + # Adjust the separator to ensure it matches the column widths exactly + separator = max_widths.map { |width| ':' + '-' * (width) }.join('-|') + + puts "| #{header} |" + puts "|#{separator}-|" + + @pkgs.each do |pkg| + puts '| ' + fields.map.with_index { |key, index| + val = get_field_value(pkg, key) + + formatting_length = val.length - val.gsub(BASH_FORMATTING_REGEX, '').length + val.ljust(max_widths[index] + formatting_length) + }.join(' | ') + ' |' + end + end + + private + + def get_field_value(pkg, field) + case field + when :groups + pkg.group_list + when :risk_type + Formatter::Risk.new(pkg.risk_type).format + when :version + Formatter::Version.new(pkg.version, pkg.latest_version).format + when :vulnerabilities + Formatter::Vulnerability.new(pkg.vulnerabilities).format + when :latest_version_date + Formatter::VersionDate.new(pkg.latest_version_date).format + else + pkg.send(field) || '' + end + end end end end diff --git a/sig/package/audit/enum/option.rbs b/sig/package/audit/enum/option.rbs index 2277240..ed404be 100644 --- a/sig/package/audit/enum/option.rbs +++ b/sig/package/audit/enum/option.rbs @@ -3,8 +3,8 @@ module Package module Enum module Option CONFIG: String - CSV: String - CSV_EXCLUDE_HEADERS: String + FORMAT: String + EXCLUDE_HEADERS: String GROUP: String INCLUDE_IGNORED: String TECHNOLOGY: String From 3c8a9d414745279e8f01e4de0e39fee60c5176d6 Mon Sep 17 00:00:00 2001 From: vkononov Date: Fri, 2 Feb 2024 12:08:53 -0600 Subject: [PATCH 02/14] Everything is working. --- README.md | 7 ------ lib/package/audit/cli.rb | 6 ++--- lib/package/audit/enum/format.rb | 2 +- lib/package/audit/services/command_parser.rb | 6 ++--- lib/package/audit/services/package_printer.rb | 16 ++++++------ lib/package/audit/util/summary_printer.rb | 12 +++++---- sig/package/audit/enum/format.rbs | 12 +++++++++ sig/package/audit/util/summary_printer.rbs | 6 +++-- steep_expectations.yml | 25 ------------------- 9 files changed, 37 insertions(+), 55 deletions(-) create mode 100644 sig/package/audit/enum/format.rbs diff --git a/README.md b/README.md index a449165..89400aa 100644 --- a/README.md +++ b/README.md @@ -219,10 +219,3 @@ The gem is available as open source under the terms of the [MIT License](https:/ ## Code of Conduct Everyone interacting in the Package::Audit project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/tactica/package-audit/blob/main/CODE_OF_CONDUCT.md). - - - -| Package | Version | Latest | Latest Date | Groups | Vulnerabilities | Risk | Risk Explanation | -|--------------|---------|--------|-------------|-------------|-----------------|------|-----------------------------------------| -| rubocop-rake | 0.6.0 | 0.6.0 | 2021-06-28 | development | | low | no updates by author in over 2 years | -| rubocop | 1.60.1 | 1.60.2 | 2024-01-24 | development | | low | not at latest version | diff --git a/lib/package/audit/cli.rb b/lib/package/audit/cli.rb index 7747c43..ca9bc78 100644 --- a/lib/package/audit/cli.rb +++ b/lib/package/audit/cli.rb @@ -28,7 +28,7 @@ class CLI < Thor desc: 'Include packages ignored by a configuration file' class_option Enum::Option::FORMAT, aliases: '-f', banner: Enum::Format.all.join('|'), type: :string, - desc: "Output reports using a different format (e.g. CSV or Markdown)" + desc: 'Output reports using a different format (e.g. CSV or Markdown)' class_option Enum::Option::EXCLUDE_HEADERS, type: :boolean, default: false, desc: "Hide headers when using the --#{Enum::Option::FORMAT} option" @@ -83,8 +83,8 @@ def respond_to_missing? def within_rescue_block yield - # rescue StandardError => e - # exit_with_error "#{e.class}: #{e.message}" + # rescue StandardError => e + # exit_with_error "#{e.class}: #{e.message}" end def exit_with_error(msg) diff --git a/lib/package/audit/enum/format.rb b/lib/package/audit/enum/format.rb index df7a663..5a8018a 100644 --- a/lib/package/audit/enum/format.rb +++ b/lib/package/audit/enum/format.rb @@ -3,7 +3,7 @@ module Audit module Enum module Format CSV = 'csv' - MD = 'md' + MARKDOWN = 'md' def self.all constants.map { |key| const_get(key) }.sort diff --git a/lib/package/audit/services/command_parser.rb b/lib/package/audit/services/command_parser.rb index 8dd2ccc..46bcb64 100644 --- a/lib/package/audit/services/command_parser.rb +++ b/lib/package/audit/services/command_parser.rb @@ -78,7 +78,7 @@ def print_results(technology, pkgs, ignored_pkgs) def print_summary(technology, pkgs, ignored_pkgs) if @report == Enum::Report::ALL - Util::SummaryPrinter.statistics(technology, @report, pkgs, ignored_pkgs) + Util::SummaryPrinter.statistics(@options[Enum::Option::FORMAT], technology, @report, pkgs, ignored_pkgs) else Util::SummaryPrinter.total(technology, @report, pkgs, ignored_pkgs) end @@ -116,8 +116,8 @@ def parse_config_file! def validate_format! format = @options[Enum::Option::FORMAT] - raise ArgumentError, "Invalid format: #{format}, should be one of [#{Enum::Format.all.join('|')}]" unless - @options[Enum::Option::FORMAT].nil? || Enum::Format.all.include?(format) + raise ArgumentError, "Invalid format: #{format}, should be one of [#{Enum::Format.all.join('|')}]" unless + @options[Enum::Option::FORMAT].nil? || Enum::Format.all.include?(format) end def parse_technologies! diff --git a/lib/package/audit/services/package_printer.rb b/lib/package/audit/services/package_printer.rb index c830c06..df20f0c 100644 --- a/lib/package/audit/services/package_printer.rb +++ b/lib/package/audit/services/package_printer.rb @@ -12,7 +12,7 @@ class PackagePrinter COLUMN_GAP = 2 - def initialize( options, pkgs) + def initialize(options, pkgs) @options = options @pkgs = pkgs end @@ -24,7 +24,7 @@ def print(fields) case @options[Enum::Option::FORMAT] when Enum::Format::CSV csv(fields, exclude_headers: @options[Enum::Option::EXCLUDE_HEADERS]) - when Enum::Format::MD + when Enum::Format::MARKDOWN markdown(fields) else pretty(fields) @@ -99,20 +99,20 @@ def csv(fields, exclude_headers: false) def markdown(fields) # Calculate the maximum width for each column, including header titles and content max_widths = fields.map do |field| - max_content_width = [@pkgs.map { |pkg| + max_content_width = [@pkgs.map do |pkg| value = get_field_value(pkg, field).to_s.gsub(BASH_FORMATTING_REGEX, '').length value - }.max, Const::Fields::HEADERS[field].gsub(BASH_FORMATTING_REGEX, '').length].max + end.max, Const::Fields::HEADERS[field].gsub(BASH_FORMATTING_REGEX, '').length].max max_content_width end # Construct the header with padding - header = fields.map.with_index { |field, index| + header = fields.map.with_index do |field, index| Const::Fields::HEADERS[field].gsub(BASH_FORMATTING_REGEX, '').ljust(max_widths[index]) - }.join(" | ") + end.join(' | ') # Adjust the separator to ensure it matches the column widths exactly - separator = max_widths.map { |width| ':' + '-' * (width) }.join('-|') + separator = max_widths.map { |width| ":#{'-' * width}" }.join('-|') puts "| #{header} |" puts "|#{separator}-|" @@ -127,8 +127,6 @@ def markdown(fields) end end - private - def get_field_value(pkg, field) case field when :groups diff --git a/lib/package/audit/util/summary_printer.rb b/lib/package/audit/util/summary_printer.rb index 7468b84..c954300 100644 --- a/lib/package/audit/util/summary_printer.rb +++ b/lib/package/audit/util/summary_printer.rb @@ -33,9 +33,9 @@ def self.total(technology, report, pkgs, ignored_pkgs) end end - def self.statistics(technology, report, pkgs, ignored_pkgs) + def self.statistics(format, technology, report, pkgs, ignored_pkgs) stats = calculate_statistics(pkgs, ignored_pkgs) - display_results(technology, report, pkgs, ignored_pkgs, stats) + display_results(format, technology, report, pkgs, ignored_pkgs, stats) end private_class_method def self.calculate_statistics(pkgs, ignored_pkgs) @@ -56,12 +56,14 @@ def self.statistics(technology, report, pkgs, ignored_pkgs) pkgs.count(&status) end - private_class_method def self.display_results(technology, report, pkgs, ignored_pkgs, stats) + private_class_method def self.display_results(format, technology, report, pkgs, ignored_pkgs, stats) if pkgs.any? - puts status_message(stats) + print status_message(stats) + puts Util::BashColor.cyan(' \\') if format == Enum::Format::MARKDOWN total(technology, report, pkgs, ignored_pkgs) elsif ignored_pkgs.any? - puts status_message(stats) + print status_message(stats) + puts Util::BashColor.cyan(' \\') if format == Enum::Format::MARKDOWN puts Util::BashColor.green("There are no deprecated, outdated or vulnerable #{technology} " \ "packages (#{ignored_pkgs.length} ignored)!\n") else diff --git a/sig/package/audit/enum/format.rbs b/sig/package/audit/enum/format.rbs new file mode 100644 index 0000000..1df0d34 --- /dev/null +++ b/sig/package/audit/enum/format.rbs @@ -0,0 +1,12 @@ +module Package + module Audit + module Enum + module Format + CSV: String + MARKDOWN: String + + def self.all: -> Array[String] + end + end + end +end diff --git a/sig/package/audit/util/summary_printer.rbs b/sig/package/audit/util/summary_printer.rbs index c82badd..59ddd62 100644 --- a/sig/package/audit/util/summary_printer.rbs +++ b/sig/package/audit/util/summary_printer.rbs @@ -4,13 +4,15 @@ module Package module SummaryPrinter def self.all: -> void + def self.calculate_statistics: (Array[Package], Array[Package]) -> Hash[Symbol, Integer] + def self.count_status: (Array[Package], Symbol) -> Integer def self.deprecated: -> void - def self.display_results: (String, Symbol, Array[Package], Array[Package], Hash[Symbol, Integer]) -> void + def self.display_results: (String, String, Symbol, Array[Package], Array[Package], Hash[Symbol, Integer]) -> void - def self.statistics: (String, Symbol, Array[Package], Array[Package]) -> void + def self.statistics: (String, String, Symbol, Array[Package], Array[Package]) -> void def self.status_message: (Hash[Symbol, Integer]) -> String diff --git a/steep_expectations.yml b/steep_expectations.yml index 1e12ca9..525d273 100644 --- a/steep_expectations.yml +++ b/steep_expectations.yml @@ -145,28 +145,3 @@ severity: WARNING message: 'Cannot find the declaration of constant: `Bundler`' code: Ruby::UnknownConstant -- file: lib/package/audit/util/summary_printer.rb - diagnostics: - - range: - start: - line: 37 - character: 18 - end: - line: 37 - character: 38 - severity: ERROR - message: Type `singleton(::Package::Audit::Util::SummaryPrinter)` does not have - method `calculate_statistics` - code: Ruby::NoMethod - - range: - start: - line: 56 - character: 21 - end: - line: 56 - character: 28 - severity: WARNING - message: |- - Cannot pass a value of type `::Proc` as a block-pass-argument of type `^(::Package::Audit::Package) -> ::boolish` - ::Proc <: ^(::Package::Audit::Package) -> ::boolish - code: Ruby::BlockTypeMismatch From 72466ebe34f1d5227a0b3b4082f5dc7ba7acb08b Mon Sep 17 00:00:00 2001 From: vkononov Date: Fri, 2 Feb 2024 12:23:51 -0600 Subject: [PATCH 03/14] The formatting is perfect now. --- lib/package/audit/services/package_printer.rb | 44 ++++++++----------- lib/package/audit/util/summary_printer.rb | 6 ++- 2 files changed, 23 insertions(+), 27 deletions(-) diff --git a/lib/package/audit/services/package_printer.rb b/lib/package/audit/services/package_printer.rb index df20f0c..57342b3 100644 --- a/lib/package/audit/services/package_printer.rb +++ b/lib/package/audit/services/package_printer.rb @@ -43,39 +43,33 @@ def check_fields(fields) end def pretty(fields = Const::Fields::DEFAULT) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity - # find the maximum length of each field across all the packages so we know how many - # characters of horizontal space to allocate for each field when printing - fields.each do |key| - instance_variable_set :"@max_#{key}", Const::Fields::HEADERS[key].length - @pkgs.each do |gem| - curr_field_length = case key - when :vulnerabilities - gem.vulnerabilities_grouped.length - when :groups - gem.group_list.length - else - gem.send(key)&.gsub(BASH_FORMATTING_REGEX, '')&.length || 0 - end - max_field_length = instance_variable_get :"@max_#{key}" - instance_variable_set :"@max_#{key}", [curr_field_length, max_field_length].max - end + # Calculate the maximum width for each column, including header titles and content + max_widths = fields.map do |field| + max_content_width = [@pkgs.map do |pkg| + value = get_field_value(pkg, field).to_s.gsub(BASH_FORMATTING_REGEX, '').length + value + end.max, Const::Fields::HEADERS[field].gsub(BASH_FORMATTING_REGEX, '').length].max + max_content_width end - line_length = fields.sum { |key| instance_variable_get :"@max_#{key}" } + - (COLUMN_GAP * (fields.length - 1)) + # Construct the header with padding + header = fields.map.with_index do |field, index| + Const::Fields::HEADERS[field].gsub(BASH_FORMATTING_REGEX, '').ljust(max_widths[index]) + end.join(' ' * COLUMN_GAP) - puts '=' * line_length - puts fields.map { |key| - Const::Fields::HEADERS[key].gsub(BASH_FORMATTING_REGEX, '').ljust(instance_variable_get(:"@max_#{key}")) - }.join(' ' * COLUMN_GAP) - puts '=' * line_length + # Adjust the separator to ensure it matches the column widths exactly + separator = max_widths.map { |width| '=' * width }.join('=' * COLUMN_GAP) + + puts separator + puts header + puts separator @pkgs.each do |pkg| - puts fields.map { |key| + puts fields.map.with_index { |key, index| val = get_field_value(pkg, key) formatting_length = val.length - val.gsub(BASH_FORMATTING_REGEX, '').length - val.ljust(instance_variable_get(:"@max_#{key}") + formatting_length) + val.ljust(max_widths[index] + formatting_length) }.join(' ' * COLUMN_GAP) end end diff --git a/lib/package/audit/util/summary_printer.rb b/lib/package/audit/util/summary_printer.rb index c954300..92004e9 100644 --- a/lib/package/audit/util/summary_printer.rb +++ b/lib/package/audit/util/summary_printer.rb @@ -59,11 +59,13 @@ def self.statistics(format, technology, report, pkgs, ignored_pkgs) private_class_method def self.display_results(format, technology, report, pkgs, ignored_pkgs, stats) if pkgs.any? print status_message(stats) - puts Util::BashColor.cyan(' \\') if format == Enum::Format::MARKDOWN + print Util::BashColor.cyan(' \\') if format == Enum::Format::MARKDOWN + puts total(technology, report, pkgs, ignored_pkgs) elsif ignored_pkgs.any? print status_message(stats) - puts Util::BashColor.cyan(' \\') if format == Enum::Format::MARKDOWN + print Util::BashColor.cyan(' \\') if format == Enum::Format::MARKDOWN + puts puts Util::BashColor.green("There are no deprecated, outdated or vulnerable #{technology} " \ "packages (#{ignored_pkgs.length} ignored)!\n") else From 3eae04eec882e4613f0501fc2d468ea686c8e158 Mon Sep 17 00:00:00 2001 From: vkononov Date: Fri, 2 Feb 2024 12:35:42 -0600 Subject: [PATCH 04/14] Things are working again. --- lib/package/audit/services/package_printer.rb | 40 +++++++------------ .../audit/services/package_printer.rbs | 2 + 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/lib/package/audit/services/package_printer.rb b/lib/package/audit/services/package_printer.rb index 57342b3..f836941 100644 --- a/lib/package/audit/services/package_printer.rb +++ b/lib/package/audit/services/package_printer.rb @@ -43,21 +43,10 @@ def check_fields(fields) end def pretty(fields = Const::Fields::DEFAULT) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity - # Calculate the maximum width for each column, including header titles and content - max_widths = fields.map do |field| - max_content_width = [@pkgs.map do |pkg| - value = get_field_value(pkg, field).to_s.gsub(BASH_FORMATTING_REGEX, '').length - value - end.max, Const::Fields::HEADERS[field].gsub(BASH_FORMATTING_REGEX, '').length].max - max_content_width - end - - # Construct the header with padding + max_widths = get_field_max_widths(fields) header = fields.map.with_index do |field, index| Const::Fields::HEADERS[field].gsub(BASH_FORMATTING_REGEX, '').ljust(max_widths[index]) end.join(' ' * COLUMN_GAP) - - # Adjust the separator to ensure it matches the column widths exactly separator = max_widths.map { |width| '=' * width }.join('=' * COLUMN_GAP) puts separator @@ -91,33 +80,32 @@ def csv(fields, exclude_headers: false) end def markdown(fields) - # Calculate the maximum width for each column, including header titles and content - max_widths = fields.map do |field| - max_content_width = [@pkgs.map do |pkg| - value = get_field_value(pkg, field).to_s.gsub(BASH_FORMATTING_REGEX, '').length - value - end.max, Const::Fields::HEADERS[field].gsub(BASH_FORMATTING_REGEX, '').length].max - max_content_width - end - - # Construct the header with padding + max_widths = get_field_max_widths(fields) header = fields.map.with_index do |field, index| Const::Fields::HEADERS[field].gsub(BASH_FORMATTING_REGEX, '').ljust(max_widths[index]) end.join(' | ') - - # Adjust the separator to ensure it matches the column widths exactly separator = max_widths.map { |width| ":#{'-' * width}" }.join('-|') puts "| #{header} |" puts "|#{separator}-|" @pkgs.each do |pkg| - puts '| ' + fields.map.with_index { |key, index| + puts "| #{fields.map.with_index { |key, index| val = get_field_value(pkg, key) formatting_length = val.length - val.gsub(BASH_FORMATTING_REGEX, '').length val.ljust(max_widths[index] + formatting_length) - }.join(' | ') + ' |' + }.join(' | ')} |" + end + end + + def get_field_max_widths(fields) + # Calculate the maximum width for each column, including header titles and content + fields.map do |field| + [@pkgs.map do |pkg| + value = get_field_value(pkg, field).to_s.gsub(BASH_FORMATTING_REGEX, '').length + value + end.max, Const::Fields::HEADERS[field].gsub(BASH_FORMATTING_REGEX, '').length].max end end diff --git a/sig/package/audit/services/package_printer.rbs b/sig/package/audit/services/package_printer.rbs index e89c40d..22c232d 100644 --- a/sig/package/audit/services/package_printer.rbs +++ b/sig/package/audit/services/package_printer.rbs @@ -18,6 +18,8 @@ module Package def csv: (Array[Symbol], ?exclude_headers: bool) -> void + def get_field_max_widths: (Array[Symbol]) -> Array[Integer] + def pretty: (?Array[Symbol]) -> void end end From d5691662550d82eda9b20d9ccef617923a05e9ae Mon Sep 17 00:00:00 2001 From: vkononov Date: Fri, 2 Feb 2024 12:48:25 -0600 Subject: [PATCH 05/14] Fix a few more things. --- lib/package/audit/services/package_printer.rb | 22 +++++-------------- .../audit/services/package_printer.rbs | 4 +++- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/lib/package/audit/services/package_printer.rb b/lib/package/audit/services/package_printer.rb index f836941..cdcf092 100644 --- a/lib/package/audit/services/package_printer.rb +++ b/lib/package/audit/services/package_printer.rb @@ -42,7 +42,7 @@ def check_fields(fields) "Available fields names are: #{Const::Fields::DEFAULT}." end - def pretty(fields = Const::Fields::DEFAULT) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity + def pretty(fields = Const::Fields::DEFAULT) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength max_widths = get_field_max_widths(fields) header = fields.map.with_index do |field, index| Const::Fields::HEADERS[field].gsub(BASH_FORMATTING_REGEX, '').ljust(max_widths[index]) @@ -56,30 +56,20 @@ def pretty(fields = Const::Fields::DEFAULT) # rubocop:disable Metrics/AbcSize, M @pkgs.each do |pkg| puts fields.map.with_index { |key, index| val = get_field_value(pkg, key) - formatting_length = val.length - val.gsub(BASH_FORMATTING_REGEX, '').length val.ljust(max_widths[index] + formatting_length) }.join(' ' * COLUMN_GAP) end end - def csv(fields, exclude_headers: false) - value_fields = fields.map do |field| - case field - when :groups - :group_list - when :vulnerabilities - :vulnerabilities_grouped - else - field - end - end - + def csv(fields = Const::Fields::DEFAULT, exclude_headers: false) # puts fields.join(',') unless exclude_headers - @pkgs.map { |gem| puts gem.to_csv(value_fields) } + @pkgs.map do |pkg| + puts fields.map { |field| get_field_value(pkg, field) }.join(',').gsub(BASH_FORMATTING_REGEX, '') + end end - def markdown(fields) + def markdown(fields = Const::Fields::DEFAULT) # max_widths = get_field_max_widths(fields) header = fields.map.with_index do |field, index| Const::Fields::HEADERS[field].gsub(BASH_FORMATTING_REGEX, '').ljust(max_widths[index]) diff --git a/sig/package/audit/services/package_printer.rbs b/sig/package/audit/services/package_printer.rbs index 22c232d..fad688d 100644 --- a/sig/package/audit/services/package_printer.rbs +++ b/sig/package/audit/services/package_printer.rbs @@ -20,7 +20,9 @@ module Package def get_field_max_widths: (Array[Symbol]) -> Array[Integer] - def pretty: (?Array[Symbol]) -> void + def markdown: (Array[Symbol]) -> void + + def pretty: (Array[Symbol]) -> void end end end From 45e28a031ebd2cbc86eabd8be738b9c7d623e297 Mon Sep 17 00:00:00 2001 From: vkononov Date: Fri, 2 Feb 2024 12:51:17 -0600 Subject: [PATCH 06/14] Adjusted the CLI options. --- lib/package/audit/cli.rb | 4 ++-- lib/package/audit/enum/option.rb | 2 +- lib/package/audit/services/package_printer.rb | 2 +- sig/package/audit/enum/option.rbs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/package/audit/cli.rb b/lib/package/audit/cli.rb index ca9bc78..76d51f6 100644 --- a/lib/package/audit/cli.rb +++ b/lib/package/audit/cli.rb @@ -29,9 +29,9 @@ class CLI < Thor class_option Enum::Option::FORMAT, aliases: '-f', banner: Enum::Format.all.join('|'), type: :string, desc: 'Output reports using a different format (e.g. CSV or Markdown)' - class_option Enum::Option::EXCLUDE_HEADERS, + class_option Enum::Option::CSV_EXCLUDE_HEADERS, type: :boolean, default: false, - desc: "Hide headers when using the --#{Enum::Option::FORMAT} option" + desc: "Hide headers when using the #{Enum::Format::CSV} format" map '-v' => :version map '--version' => :version diff --git a/lib/package/audit/enum/option.rb b/lib/package/audit/enum/option.rb index 6acfeb4..2464360 100644 --- a/lib/package/audit/enum/option.rb +++ b/lib/package/audit/enum/option.rb @@ -4,7 +4,7 @@ module Enum module Option CONFIG = 'config' FORMAT = 'format' - EXCLUDE_HEADERS = 'exclude-headers' + CSV_EXCLUDE_HEADERS = 'exclude-headers' GROUP = 'group' INCLUDE_IGNORED = 'include-ignored' TECHNOLOGY = 'technology' diff --git a/lib/package/audit/services/package_printer.rb b/lib/package/audit/services/package_printer.rb index cdcf092..aa833d2 100644 --- a/lib/package/audit/services/package_printer.rb +++ b/lib/package/audit/services/package_printer.rb @@ -23,7 +23,7 @@ def print(fields) case @options[Enum::Option::FORMAT] when Enum::Format::CSV - csv(fields, exclude_headers: @options[Enum::Option::EXCLUDE_HEADERS]) + csv(fields, exclude_headers: @options[Enum::Option::CSV_EXCLUDE_HEADERS]) when Enum::Format::MARKDOWN markdown(fields) else diff --git a/sig/package/audit/enum/option.rbs b/sig/package/audit/enum/option.rbs index ed404be..f86e5e4 100644 --- a/sig/package/audit/enum/option.rbs +++ b/sig/package/audit/enum/option.rbs @@ -4,7 +4,7 @@ module Package module Option CONFIG: String FORMAT: String - EXCLUDE_HEADERS: String + CSV_EXCLUDE_HEADERS: String GROUP: String INCLUDE_IGNORED: String TECHNOLOGY: String From aeb3253811489cf5197bd8baf2bcbfc11f51b4b5 Mon Sep 17 00:00:00 2001 From: vkononov Date: Fri, 2 Feb 2024 12:54:30 -0600 Subject: [PATCH 07/14] Markdown is working. --- lib/package/audit/services/package_printer.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/package/audit/services/package_printer.rb b/lib/package/audit/services/package_printer.rb index aa833d2..d7728db 100644 --- a/lib/package/audit/services/package_printer.rb +++ b/lib/package/audit/services/package_printer.rb @@ -80,12 +80,12 @@ def markdown(fields = Const::Fields::DEFAULT) # puts "|#{separator}-|" @pkgs.each do |pkg| - puts "| #{fields.map.with_index { |key, index| + row = fields.map.with_index do |key, index| val = get_field_value(pkg, key) - formatting_length = val.length - val.gsub(BASH_FORMATTING_REGEX, '').length val.ljust(max_widths[index] + formatting_length) - }.join(' | ')} |" + end + puts "| #{row.join(' | ')} |" end end From 06ab72ebc90b887093c88b3ce9f7302a2440513a Mon Sep 17 00:00:00 2001 From: vkononov Date: Fri, 2 Feb 2024 12:54:59 -0600 Subject: [PATCH 08/14] Fix lint errors. --- lib/package/audit/services/package_printer.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/package/audit/services/package_printer.rb b/lib/package/audit/services/package_printer.rb index d7728db..5955b21 100644 --- a/lib/package/audit/services/package_printer.rb +++ b/lib/package/audit/services/package_printer.rb @@ -62,14 +62,14 @@ def pretty(fields = Const::Fields::DEFAULT) # rubocop:disable Metrics/AbcSize, M end end - def csv(fields = Const::Fields::DEFAULT, exclude_headers: false) # + def csv(fields = Const::Fields::DEFAULT, exclude_headers: false) puts fields.join(',') unless exclude_headers @pkgs.map do |pkg| puts fields.map { |field| get_field_value(pkg, field) }.join(',').gsub(BASH_FORMATTING_REGEX, '') end end - def markdown(fields = Const::Fields::DEFAULT) # + def markdown(fields = Const::Fields::DEFAULT) max_widths = get_field_max_widths(fields) header = fields.map.with_index do |field, index| Const::Fields::HEADERS[field].gsub(BASH_FORMATTING_REGEX, '').ljust(max_widths[index]) @@ -84,7 +84,7 @@ def markdown(fields = Const::Fields::DEFAULT) # val = get_field_value(pkg, key) formatting_length = val.length - val.gsub(BASH_FORMATTING_REGEX, '').length val.ljust(max_widths[index] + formatting_length) - end + end puts "| #{row.join(' | ')} |" end end From 68a573bcf1d04fa9280b7c12b360b27b894eecb9 Mon Sep 17 00:00:00 2001 From: vkononov Date: Fri, 2 Feb 2024 12:57:45 -0600 Subject: [PATCH 09/14] Fix more lint errors. --- lib/package/audit/services/package_printer.rb | 4 ++-- sig/package/audit/services/package_printer.rbs | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/package/audit/services/package_printer.rb b/lib/package/audit/services/package_printer.rb index 5955b21..249ffbd 100644 --- a/lib/package/audit/services/package_printer.rb +++ b/lib/package/audit/services/package_printer.rb @@ -69,7 +69,7 @@ def csv(fields = Const::Fields::DEFAULT, exclude_headers: false) end end - def markdown(fields = Const::Fields::DEFAULT) + def markdown(fields = Const::Fields::DEFAULT) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength max_widths = get_field_max_widths(fields) header = fields.map.with_index do |field, index| Const::Fields::HEADERS[field].gsub(BASH_FORMATTING_REGEX, '').ljust(max_widths[index]) @@ -99,7 +99,7 @@ def get_field_max_widths(fields) end end - def get_field_value(pkg, field) + def get_field_value(pkg, field) # rubocop:disable Metrics/MethodLength case field when :groups pkg.group_list diff --git a/sig/package/audit/services/package_printer.rbs b/sig/package/audit/services/package_printer.rbs index fad688d..cfde4e5 100644 --- a/sig/package/audit/services/package_printer.rbs +++ b/sig/package/audit/services/package_printer.rbs @@ -20,6 +20,8 @@ module Package def get_field_max_widths: (Array[Symbol]) -> Array[Integer] + def get_field_value: (Package, Symbol) -> String + def markdown: (Array[Symbol]) -> void def pretty: (Array[Symbol]) -> void From 567561a33576592eac6b3c2d33eb4310240bd2df Mon Sep 17 00:00:00 2001 From: vkononov Date: Fri, 2 Feb 2024 14:21:37 -0600 Subject: [PATCH 10/14] Fix all lint errors. --- lib/package/audit/util/summary_printer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/package/audit/util/summary_printer.rb b/lib/package/audit/util/summary_printer.rb index 92004e9..84bbb1a 100644 --- a/lib/package/audit/util/summary_printer.rb +++ b/lib/package/audit/util/summary_printer.rb @@ -56,7 +56,7 @@ def self.statistics(format, technology, report, pkgs, ignored_pkgs) pkgs.count(&status) end - private_class_method def self.display_results(format, technology, report, pkgs, ignored_pkgs, stats) + private_class_method def self.display_results(format, technology, report, pkgs, ignored_pkgs, stats) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/ParameterLists if pkgs.any? print status_message(stats) print Util::BashColor.cyan(' \\') if format == Enum::Format::MARKDOWN From f65d0874ae3d083fbab5ea3e083b8f573975c609 Mon Sep 17 00:00:00 2001 From: vkononov Date: Fri, 2 Feb 2024 14:30:13 -0600 Subject: [PATCH 11/14] Update RBS. --- sig/package/audit/services/command_parser.rbs | 6 +++- steep_expectations.yml | 29 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/sig/package/audit/services/command_parser.rbs b/sig/package/audit/services/command_parser.rbs index be629ce..3687373 100644 --- a/sig/package/audit/services/command_parser.rbs +++ b/sig/package/audit/services/command_parser.rbs @@ -17,10 +17,12 @@ module Package def learn_more_command: (String) -> String? - def parse_config_file: -> Hash[String, untyped]? + def parse_config_file!: -> Hash[String, untyped]? def parse_technologies: -> Array[String] + def parse_technologies!: -> Array[String] + def print_disclaimer: (String) -> void def print_results: (String, Array[Package], Array[Package]) -> void @@ -28,6 +30,8 @@ module Package def print_summary: (String, Array[Package], Array[Package]) -> void def process_technologies: -> int + + def validate_format!: -> void end end end diff --git a/steep_expectations.yml b/steep_expectations.yml index 525d273..40e4a78 100644 --- a/steep_expectations.yml +++ b/steep_expectations.yml @@ -145,3 +145,32 @@ severity: WARNING message: 'Cannot find the declaration of constant: `Bundler`' code: Ruby::UnknownConstant +- file: lib/package/audit/services/package_printer.rb + diagnostics: + - range: + start: + line: 94 + character: 19 + end: + line: 99 + character: 11 + severity: WARNING + message: |- + Cannot allow block body have type `(::Integer | nil)` because declared as type `::Integer` + (::Integer | nil) <: ::Integer + nil <: ::Integer + code: Ruby::BlockBodyTypeMismatch +- file: lib/package/audit/util/summary_printer.rb + diagnostics: + - range: + start: + line: 56 + character: 21 + end: + line: 56 + character: 28 + severity: WARNING + message: |- + Cannot pass a value of type `::Proc` as a block-pass-argument of type `^(::Package::Audit::Package) -> ::boolish` + ::Proc <: ^(::Package::Audit::Package) -> ::boolish + code: Ruby::BlockTypeMismatch From 358599307332c4f223cdb4abfb0d06e97030d3e3 Mon Sep 17 00:00:00 2001 From: vkononov Date: Fri, 2 Feb 2024 14:49:58 -0600 Subject: [PATCH 12/14] Add certain tests. --- lib/package/audit/cli.rb | 4 +-- test/package/audit/test_cli.rb | 59 ++++++++++++++++++++++++++++++++++ test/package/test_audit.rb | 18 ----------- 3 files changed, 61 insertions(+), 20 deletions(-) create mode 100644 test/package/audit/test_cli.rb diff --git a/lib/package/audit/cli.rb b/lib/package/audit/cli.rb index 76d51f6..b325593 100644 --- a/lib/package/audit/cli.rb +++ b/lib/package/audit/cli.rb @@ -83,8 +83,8 @@ def respond_to_missing? def within_rescue_block yield - # rescue StandardError => e - # exit_with_error "#{e.class}: #{e.message}" + rescue StandardError => e + exit_with_error "#{e.class}: #{e.message}" end def exit_with_error(msg) diff --git a/test/package/audit/test_cli.rb b/test/package/audit/test_cli.rb new file mode 100644 index 0000000..046871c --- /dev/null +++ b/test/package/audit/test_cli.rb @@ -0,0 +1,59 @@ +require 'test_helper' +require_relative '../../../lib/package/audit/enum/format' +require_relative '../../../lib/package/audit/enum/technology' +require_relative '../../../lib/package/audit/util/risk_legend' +require_relative '../../../lib/package/audit/version' +require 'bundler' + +module Package + module Audit + class Cli < Minitest::Test + def test_that_it_the_version_command_works + output = `bundle exec package-audit --version` + + assert_match Package::Audit::VERSION, output + end + + def test_that_it_the_risk_command_works + output = `bundle exec package-audit risk` + + stdout = StringIO.new + $stdout = stdout + Package::Audit::Util::RiskLegend.print + $stdout = STDOUT + + assert_equal stdout.string, output + end + + def test_that_help_command_works + output = `bundle exec package-audit help` + + assert_match 'Commands:', output + end + + def test_that_that_exclude_headers_option_works + output = `bundle exec package-audit test/files/gemfile/empty --exclude-headers` + + assert_match 'There are no deprecated, outdated or vulnerable ruby packages!', output + end + + def test_that_that_format_option_works + output = `bundle exec package-audit test/files/gemfile/empty --format invalid` + + assert_match "Invalid format: invalid, should be one of [#{Enum::Format.all.join('|')}]", output + end + + def test_that_that_format_option_alias_works + output = `bundle exec package-audit test/files/gemfile/empty -f invalid` + + assert_match "Invalid format: invalid, should be one of [#{Enum::Format.all.join('|')}]", output + end + + def test_that_that_technology_option_works + output = `bundle exec package-audit test/files/gemfile/empty --technology invalid` + + assert_match "\"invalid\" is not a supported technology, use one of #{Enum::Technology.all}", output + end + end + end +end diff --git a/test/package/test_audit.rb b/test/package/test_audit.rb index ac0a3bb..4638143 100644 --- a/test/package/test_audit.rb +++ b/test/package/test_audit.rb @@ -1,5 +1,4 @@ require 'test_helper' -require_relative '../../lib/package/audit/util/risk_legend' require_relative '../../lib/package/audit/util/summary_printer' require_relative '../../lib/package/audit/version' @@ -11,23 +10,6 @@ def test_that_it_has_a_version_number refute_nil Package::Audit::VERSION end - def test_that_it_returns_a_version_number - output = `bundle exec package-audit --version` - - assert_match Package::Audit::VERSION, output - end - - def test_that_it_prints_risk_information - output = `bundle exec package-audit risk` - - stdout = StringIO.new - $stdout = stdout - Package::Audit::Util::RiskLegend.print - $stdout = STDOUT - - assert_equal stdout.string, output - end - def test_that_there_is_a_success_message_when_report_is_empty output = `bundle exec package-audit test/files/gemfile/empty` From 04a9419c7b07df78cd9a36f9581ef2d9137ff932 Mon Sep 17 00:00:00 2001 From: vkononov Date: Fri, 2 Feb 2024 16:35:27 -0600 Subject: [PATCH 13/14] Fix all automated tests. --- test/package/audit/test_cli.rb | 16 ++++++++++++++-- test/package/audit/test_package.rb | 2 +- test/package/audit/test_printer.rb | 26 +++++++++++++++++++++++--- 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/test/package/audit/test_cli.rb b/test/package/audit/test_cli.rb index 046871c..2c6ae2c 100644 --- a/test/package/audit/test_cli.rb +++ b/test/package/audit/test_cli.rb @@ -11,7 +11,7 @@ class Cli < Minitest::Test def test_that_it_the_version_command_works output = `bundle exec package-audit --version` - assert_match Package::Audit::VERSION, output + assert_match VERSION, output end def test_that_it_the_risk_command_works @@ -19,7 +19,7 @@ def test_that_it_the_risk_command_works stdout = StringIO.new $stdout = stdout - Package::Audit::Util::RiskLegend.print + Util::RiskLegend.print $stdout = STDOUT assert_equal stdout.string, output @@ -31,6 +31,18 @@ def test_that_help_command_works assert_match 'Commands:', output end + def test_that_unknown_commands_give_an_appropriate_error + output = `bundle exec package-audit invalid` + + assert_match '"invalid" is not a valid directory', output + end + + def test_that_that_include_ignored_option_works + output = `bundle exec package-audit test/files/gemfile/empty --include-ignored` + + assert_match 'There are no deprecated, outdated or vulnerable ruby packages!', output + end + def test_that_that_exclude_headers_option_works output = `bundle exec package-audit test/files/gemfile/empty --exclude-headers` diff --git a/test/package/audit/test_package.rb b/test/package/audit/test_package.rb index 342b139..a4ad131 100644 --- a/test/package/audit/test_package.rb +++ b/test/package/audit/test_package.rb @@ -20,7 +20,7 @@ def test_that_the_full_name_field_works_as_expected end def test_that_the_group_list_is_properly_delimited - assert_equal @package.groups.join('|'), @package.group_list + assert_equal @package.groups.join(' '), @package.group_list end def test_that_grouped_vulnerabilities_are_formatted_correctly diff --git a/test/package/audit/test_printer.rb b/test/package/audit/test_printer.rb index 14da76f..c1fe2eb 100644 --- a/test/package/audit/test_printer.rb +++ b/test/package/audit/test_printer.rb @@ -2,6 +2,7 @@ require 'stringio' require_relative '../../../lib/package/audit/const/fields' +require_relative '../../../lib/package/audit/enum/format' require_relative '../../../lib/package/audit/util/bash_color' require_relative '../../../lib/package/audit/services/package_printer' require_relative '../../../lib/package/audit/formatter/vulnerability' @@ -46,14 +47,32 @@ def test_that_the_dependencies_are_displayed_correctly lines = @output.string.split("\n") assert_equal 6, lines.length + assert_equal '========================================', lines[0] + assert_equal 'Package Version Latest Latest Date', lines[1] + assert_equal '========================================', lines[2] assert_equal "fileutils 1.#{Util::BashColor.yellow('5.0')} 1.7.1 #{@today} ", lines[3] assert_equal "puma 5.1.1 5.1.1 #{Util::BashColor.yellow('2020-12-10')} ", lines[4] assert_equal "rails #{Util::BashColor.orange('6.0.0')} 7.0.4.3 #{@today} ", lines[5] end + def test_that_the_dependencies_are_displayed_correctly_in_markdown + options = {Enum::Option::FORMAT => Enum::Format::MARKDOWN} + PackagePrinter.new(options, @gems).print(%i[name version latest_version latest_version_date]) + lines = @output.string.split("\n") + + assert_equal 5, lines.length + assert_equal '| Package | Version | Latest | Latest Date |', lines[0] + assert_equal '|:----------|:--------|:--------|:------------|', lines[1] + assert_equal "| fileutils | 1.#{Util::BashColor.yellow('5.0')} | 1.7.1 | #{@today} |", lines[2] + assert_equal "| puma | 5.1.1 | 5.1.1 | #{Util::BashColor.yellow('2020-12-10')} |", lines[3] + assert_equal "| rails | #{Util::BashColor.orange('6.0.0')} | 7.0.4.3 | #{@today} |", lines[4] + + end + def test_that_the_dependencies_are_displayed_correctly_in_csv_with_headers # rubocop:disable Metrics/AbcSize + options = {Enum::Option::FORMAT => Enum::Format::CSV} columns = %i[name version latest_version groups] - PackagePrinter.new({ 'csv' => true}, @gems).print(columns) + PackagePrinter.new(options, @gems).print(columns) lines = @output.string.split("\n") assert_equal @gems.length + 1, lines.length @@ -72,10 +91,11 @@ def test_that_vulnerabilities_are_displayed_correctly end def test_that_vulnerabilities_are_displayed_correctly_in_csv - PackagePrinter.new({ 'csv' => true}, [@rails]).print(%i[vulnerabilities]) + options = {Enum::Option::FORMAT => Enum::Format::CSV} + PackagePrinter.new(options, [@rails]).print(%i[vulnerabilities]) lines = @output.string.split("\n") - assert_equal 'medium(1)|high(2)', lines[1] + assert_equal 'medium(1) high(2)', lines[1] end end end From 19840403fd417b65cd5a4ecc26f5a54279996eb7 Mon Sep 17 00:00:00 2001 From: vkononov Date: Fri, 2 Feb 2024 16:48:52 -0600 Subject: [PATCH 14/14] Fix all automated tests. --- README.md | 14 ++++++++++---- test/package/audit/test_cli.rb | 22 ++++++++++++++++++++-- test/package/audit/test_printer.rb | 5 ++--- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 89400aa..b877728 100644 --- a/README.md +++ b/README.md @@ -141,16 +141,22 @@ gem install package-audit package-audit --group staging --group production [DIR] ``` -* To show how risk is calculated for the above report run: +* To produce the same report in a CSV format run: ```bash - package-audit risk + package-audit --format csv ``` -* To produce the same report in a CSV format run: +* To produce the same report in a Markdown format run: + + ```bash + package-audit --format md + ``` + +* To show how risk is calculated for the above report run: ```bash - package-audit --csv + package-audit risk ``` #### For a list of all commands and their options run: diff --git a/test/package/audit/test_cli.rb b/test/package/audit/test_cli.rb index 2c6ae2c..291d9fd 100644 --- a/test/package/audit/test_cli.rb +++ b/test/package/audit/test_cli.rb @@ -8,13 +8,13 @@ module Package module Audit class Cli < Minitest::Test - def test_that_it_the_version_command_works + def test_that_it_version_command_works output = `bundle exec package-audit --version` assert_match VERSION, output end - def test_that_it_the_risk_command_works + def test_that_it_risk_command_works output = `bundle exec package-audit risk` stdout = StringIO.new @@ -37,6 +37,24 @@ def test_that_unknown_commands_give_an_appropriate_error assert_match '"invalid" is not a valid directory', output end + def test_that_that_config_option_works + output = `bundle exec package-audit test/files/gemfile/empty --config test/files/config/.package-audit.yml` + + assert_match 'There are no deprecated, outdated or vulnerable ruby packages!', output + end + + def test_that_that_config_option_alias_works + output = `bundle exec package-audit test/files/gemfile/empty -c test/files/config/.package-audit.yml` + + assert_match 'There are no deprecated, outdated or vulnerable ruby packages!', output + end + + def test_that_that_config_option_returns_an_appropriate_error + output = `bundle exec package-audit test/files/gemfile/empty -c test/files/config/invalid` + + assert_match 'Configuration file not found: test/files/config/invalid', output + end + def test_that_that_include_ignored_option_works output = `bundle exec package-audit test/files/gemfile/empty --include-ignored` diff --git a/test/package/audit/test_printer.rb b/test/package/audit/test_printer.rb index c1fe2eb..8fd88eb 100644 --- a/test/package/audit/test_printer.rb +++ b/test/package/audit/test_printer.rb @@ -42,7 +42,7 @@ def test_that_an_error_is_shown_for_invalid_fields end end - def test_that_the_dependencies_are_displayed_correctly + def test_that_the_dependencies_are_displayed_correctly # rubocop:disable Metrics/AbcSize PackagePrinter.new({}, @gems).print(%i[name version latest_version latest_version_date]) lines = @output.string.split("\n") @@ -55,7 +55,7 @@ def test_that_the_dependencies_are_displayed_correctly assert_equal "rails #{Util::BashColor.orange('6.0.0')} 7.0.4.3 #{@today} ", lines[5] end - def test_that_the_dependencies_are_displayed_correctly_in_markdown + def test_that_the_dependencies_are_displayed_correctly_in_markdown # rubocop:disable Metrics/AbcSize options = {Enum::Option::FORMAT => Enum::Format::MARKDOWN} PackagePrinter.new(options, @gems).print(%i[name version latest_version latest_version_date]) lines = @output.string.split("\n") @@ -66,7 +66,6 @@ def test_that_the_dependencies_are_displayed_correctly_in_markdown assert_equal "| fileutils | 1.#{Util::BashColor.yellow('5.0')} | 1.7.1 | #{@today} |", lines[2] assert_equal "| puma | 5.1.1 | 5.1.1 | #{Util::BashColor.yellow('2020-12-10')} |", lines[3] assert_equal "| rails | #{Util::BashColor.orange('6.0.0')} | 7.0.4.3 | #{@today} |", lines[4] - end def test_that_the_dependencies_are_displayed_correctly_in_csv_with_headers # rubocop:disable Metrics/AbcSize