# Copyright (c) 2023 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true require 'contrast/agent/telemetry/base' require 'contrast/agent/telemetry/identifier' require 'contrast/config/env_variables' module Contrast module Agent # Tools for supporting the Telemetry feature module Telemetry DIR = '/etc/contrast/ruby-agent/'.cs__freeze FILE = '.telemetry'.cs__freeze CURRENT_DIR = Dir.pwd.cs__freeze CONFIG_DIR = CURRENT_DIR + '/config/contrast/'.cs__freeze MESSAGE = { disclaimer: "\n===================================================================================================" \ "\n\nThe [Contrast Security] [Ruby Agent] " \ "collects usage data in order to help us improve compatibility\n" \ "and security coverage. The data is anonymous and does not contain application data. It is collected\n" \ "by Contrast and is never shared. You can opt-out of telemetry by setting the\n" \ "'CONTRAST_AGENT_TELEMETRY_OPTOUT' environment variable to '1' or 'true'.\n" \ 'Read more about [Contrast Security] [Ruby Agent] telemetry: ' \ "https://docs.contrastsecurity.com/en/ruby-telemetry.html \n\n" \ "===================================================================================================\n\n" }.cs__freeze # Here we create the .telemetry file. If the file exist we do nothing. # # @return[Boolean, nil] true if file is created, false if file already exist # and nil if Telemetry is disabled or on unsupported OS. def self.create_telemetry_file write_mark_file(DIR, FILE, CONFIG_DIR) end # The telemetry disclaimer message, set if needed. # # @return [String] def self.disclaimer @_disclaimer = MESSAGE[:disclaimer] end # Determine if telemetry has been explicitly disabled by opt out or has been left running. # # @return [Boolean] def self.exceptions_enabled? @_exceptions_enabled = telemetry_exceptions_enabled? if @_exceptions_enabled.nil? @_exceptions_enabled end class << self include Contrast::Config::EnvVariables private # Create the mark file # # @param dir [String] Directory in which the file is to be created # @param file [String] filename of the mark file # @param config_dir [String] path for the config folder in working directory # @return[Boolean, nil] true if file is created, false if file already exist # and nil if Telemetry is disabled or on unsupported OS. def write_mark_file dir, file, config_dir return unless Contrast::Agent::Telemetry::Base.enabled? return if Contrast::Utils::OS.windows? @dir = dir # After macOS Catalina, you can no longer store files or data in the read-only system volume, # nor can we write to the "root" directory ( / ). This results in Errno::EROFS exception. # So for the MacOS we would use the config directory of the instrumented application. @dir = config_dir if Contrast::Utils::OS.mac? return false if File.file?(@dir + file) return true if touch_marker(@dir, file) # If we don't have permission to write to root directory use config instead touch_marker(config_dir, file) end # Touches .telemetry file # # @return[Boolean] true if success, false if fails def touch_marker dir, file FileUtils.mkdir_p(dir) FileUtils.touch(dir + file) File.file?(dir + file) rescue StandardError => _e false end # Determine if telemetry has been explicitly disabled by opt out or has been left running. # # @return [Boolean] def telemetry_exceptions_enabled? return false unless Contrast::Agent::Telemetry::Base.enabled? true end end end end end