# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true require 'contrast/utils/string_utils' require 'contrast/components/logger' module Contrast module Agent module Reporting # This is the new Library Discovery class which will include all the needed information for the new # reporting system to relay this information in the Application Update messages. These libraries are used by # TeamServer to construct the dependency information for the SCA feature. They represent those gems that are # included in the GemSpec, not necessarily those that have had files in them required. # # @attr_reader class_count [Integer] the number of files in the Gem which contain code. # @attr_reader external_date [Integer] the time, in ms, when the Gem was published. Required for reporting. # @attr_reader file [String] the name of the Gem. Required for reporting. # @attr_reader hash [String] the Sha256 of the Gem, matching its hash in RubyGems. Required for reporting. # @attr_reader internal_date [Integer] the time, in ms, when the Gem was published. Required for reporting. # @attr_accessor manifest [String] the YAML form of the Gem's specification. # @attr_reader tags [String] Inventory tags set by the user via configuration. # @attr_reader url [String] The homepage of the Gem. # @attr_reader version [String] The version of the Gem. class LibraryDiscovery include Contrast::Components::Logger::InstanceMethods StringUtils = Contrast::Utils::StringUtils # required attributes attr_reader :external_date, :file, :hash, :internal_date # optional attributes attr_reader :class_count, :tags, :url, :version attr_accessor :manifest def initialize digest, spec @file = StringUtils.force_utf8(spec.name) # rubocop:disable Security/Module/Name @hash = StringUtils.force_utf8(digest) @version = StringUtils.force_utf8(spec.version) @manifest = StringUtils.force_utf8(StringUtils.force_utf8(spec.to_yaml.to_s)) @external_date = (spec.date.to_f * 1000.0).to_i @internal_date = @external_date @url = StringUtils.force_utf8(spec.homepage) @class_count = Contrast::Utils::Sha256Builder.instance.files(spec.full_gem_path.to_s).length @tags = Contrast::INVENTORY.tags end # Convert the instance variables on the class, and other information, into the identifiers required for # TeamServer to process the JSON form of this message. # # @return [Hash] # @raise [ArgumentError] def to_controlled_hash begin validate rescue ArgumentError => e logger.error('LibraryDiscovery validation failed with: ', e) return end { classCount: class_count, externalDate: external_date, file: file, hash: hash, internalDate: internal_date, manifest: manifest, url: url, version: version, tags: tags }.compact end # Ensure the required fields are present. # # @raise [ArgumentError] def validate unless external_date raise(ArgumentError, "#{ self } did not have a proper external date. Unable to continue.") end unless internal_date raise(ArgumentError, "#{ self } did not have a proper internal date. Unable to continue.") end raise(ArgumentError, "#{ self } did not have a proper file. Unable to continue.") unless file raise(ArgumentError, "#{ self } did not have a proper hash. Unable to continue.") unless hash end end end end end