postmodern/cvelist.rb

View on GitHub
lib/cvelist/year_dir.rb

Summary

Maintainability
A
0 mins
Test Coverage
# frozen_string_literal: true

require 'cvelist/directory'
require 'cvelist/exceptions'
require 'cvelist/range_dir'

module CVEList
  class YearDir < Directory

    include Enumerable

    # Path to the year directory.
    #
    # @return [String]
    attr_reader :path

    # The year of the directory.
    #
    # @return [Integer]
    attr_reader :year

    #
    # Initializes the year dir.
    #
    # @param [String] path
    #   The path to the year directory.
    #
    def initialize(path)
      super(path)

      @year = File.basename(@path).to_i
    end

    #
    # Determines if the year directory contains the given range directory.
    #
    # @param [String] xxx_range
    #   The given range directory ending in `xxx`.
    #
    # @return [Boolean]
    #
    def has_range?(xxx_range)
      directory?(xxx_range)
    end

    #
    # Access a range directory within the year directory.
    #
    # @param [String] xxx_range
    #   The "xxx" range.
    #
    # @return [RangeDir]
    #   The range directory.
    #
    # @raise [RangeDirNotFound]
    #   Could not find the given range directory within the year directory.
    #
    def range(xxx_range)
      range_dir_path = join(xxx_range)

      unless File.directory?(range_dir_path)
        raise(RangeDirNotFound,"#{xxx_range.inspect} not found within #{@path.inspect}")
      end

      return RangeDir.new(range_dir_path)
    end

    alias / range

    # `Dir.glob` pattern for `Nxxx` range directories.
    GLOB = '*xxx'

    #
    # The `xxx` number ranges within the directory.
    #
    # @return [Array<String>]
    #
    def directories
      glob(GLOB).sort
    end

    #
    # The range directories within the year directory.
    #
    # @return [Enumerator]
    #   If no block is given, an Enumerator will be returned.
    #
    def ranges(&block)
      directories.map { |dir| RangeDir.new(dir) }
    end

    #
    # Enumerates over each CVE, in each range directory, within the year
    # directory.
    #
    # @yield [cve]
    #   The given block will be passed each CVE in the year dir.
    #
    # @yieldparam [CVE] cve
    #   A CVE within one of the range directories.
    #
    # @return [Enumerator]
    #   If no block is given, an Enumerator will be returned.
    #
    def each(&block)
      return enum_for(__method__) unless block_given?

      ranges.each do |range_dir|
        range_dir.each(&block)
      end
    end

    #
    # Enumerates over every malformed CVE within the year directories.
    #
    # @yield [malformed_cve]
    #   The given block will be passed each malformed CVE from within the
    #   year directory.
    #
    # @yieldparam [MalformedCVE] malformed_cve
    #   A malformed CVE from within the year directory.
    #
    # @return [Enumerator]
    #   If no block is given, an Enumerator will be returned.
    #
    def each_malformed(&block)
      return enum_for(__method__) unless block_given?

      ranges.each do |range_dir|
        range_dir.each_malformed(&block)
      end
    end

    #
    # Determines whether a CVE exists with the given ID, within any of the range
    # directories, within the year directory.
    #
    # @param [String] cve_id
    #   The given CVE ID.
    #
    # @return [Boolean]
    #   Specifies whether the CVE exists.
    #
    def has_cve?(cve_id)
      xxx_range = cve_to_xxx_range(cve_id)

      has_range?(xxx_range) && range(xxx_range).has_cve?(cve_id)
    end

    #
    # Loads a CVE.
    #
    # @param [String] cve_id
    #   The CVE ID.
    #
    # @return [CVE, nil]
    #   The loaded CVE or `nil` if the accompanying range directory for the CVE
    #   could not be found.
    #
    def [](cve_id)
      xxx_range = cve_to_xxx_range(cve_id)

      if has_range?(xxx_range)
        range(xxx_range)[cve_id]
      end
    end

    private

    def cve_to_xxx_range(cve_id)
      cve_number = cve_id[cve_id.rindex('-')+1 ..]
      cve_number[-3,3] = 'xxx'

      return cve_number
    end

  end
end