<commit_before>
# Copyright 2012-2014 Chef Software, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

require 'omnibus/sugarable'
begin
  require 'pedump'
rescue LoadError
  STDERR.puts "pedump not found - windows health checks disabled"
end

module Omnibus
  class HealthCheck
    include Logging
    include Util
    include Sugarable

    WHITELIST_LIBS = [
      /ld-linux/,
      /libc\.so/,
      /libcrypt\.so/,
      /libdl/,
      /libfreebl\d\.so/,
      /libgcc_s\.so/,
      /libm\.so/,
      /libnsl\.so/,
      /libpthread/,
      /libresolv\.so/,
      /librt\.so/,
      /libstdc\+\+\.so/,
      /libutil\.so/,
      /linux-vdso.+/,
      /linux-gate\.so/,
    ].freeze

    ARCH_WHITELIST_LIBS = [
      /libc\.so/,
      /libcrypt\.so/,
      /libdb-5\.3\.so/,
      /libdl\.so/,
      /libffi\.so/,
      /libgdbm\.so/,
      /libm\.so/,
      /libnsl\.so/,
      /libpthread\.so/,
      /librt\.so/,
      /libutil\.so/,
    ].freeze

    AIX_WHITELIST_LIBS = [
      /libpthread\.a/,
      /libpthreads\.a/,
      /libdl.a/,
      /librtl\.a/,
      /libc\.a/,
      /libcrypt\.a/,
      /unix$/,
    ].freeze

    SOLARIS_WHITELIST_LIBS = [
      /libaio\.so/,
      /libavl\.so/,
      /libcrypt_[di]\.so/,
      /libcrypto.so/,
      /libcurses\.so/,
      /libdoor\.so/,
      /libgen\.so/,
      /libmd5\.so/,
      /libmd\.so/,
      /libmp\.so/,
      /libresolv\.so/,
      /libscf\.so/,
      /libsec\.so/,
      /libsocket\.so/,
      /libssl.so/,
      /libthread.so/,
      /libuutil\.so/,
      /libkstat\.so/,
      # solaris 11 libraries:
      /libc\.so\.1/,
      /libm\.so\.2/,
      /libdl\.so\.1/,
      /libnsl\.so\.1/,
      /libpthread\.so\.1/,
      /librt\.so\.1/,
      /libcrypt\.so\.1/,
      /libgdbm\.so\.3/,
      /libgcc_s\.so\.1/,
      /libcryptoutil\.so\.1/,
      /libucrypto\.so\.1/,
      /libz\.so\.1/, # while we package our own libz, this get dragged along from Solaris 11's libelf library for some reason...
      /libelf\.so\.1/,
      /libssp\.so\.0/,
      # solaris 9 libraries:
      /libm\.so\.1/,
      /libc_psr\.so\.1/,
      /s9_preload\.so\.1/,
    ].freeze

    SMARTOS_WHITELIST_LIBS = [
      /libm.so/,
      /libpthread.so/,
      /librt.so/,
      /libsocket.so/,
      /libdl.so/,
      /libnsl.so/,
      /libgen.so/,
      /libmp.so/,
      /libmd.so/,
      /libc.so/,
      /libgcc_s.so/,
      /libstdc\+\+\.so/,
      /libcrypt.so/,
    ].freeze

    MAC_WHITELIST_LIBS = [
      /libobjc\.A\.dylib/,
      /libSystem\.B\.dylib/,
      /CoreFoundation/,
      /CoreServices/,
      /Tcl$/,
      /Cocoa$/,
      /Carbon$/,
      /IOKit$/,
      /Tk$/,
      /libutil\.dylib/,
      /libffi\.dylib/,
      /libncurses\.5\.4\.dylib/,
      /libiconv/,
      /libstdc\+\+\.6\.dylib/,
      /libc\+\+\.1\.dylib/,
    ].freeze

    FREEBSD_WHITELIST_LIBS = [
      /libc\.so/,
      /libgcc_s\.so/,
      /libcrypt\.so/,
      /libm\.so/,
      /librt\.so/,
      /libthr\.so/,
      /libutil\.so/,
      /libelf\.so/,
      /libkvm\.so/,
      /libprocstat\.so/,
      /libmd\.so/,
    ].freeze

    IGNORED_ENDINGS = %w(
      .[ch]
      .e*rb
      .gemspec
      .gitignore
      .h*h
      .java
      .js
      .json
      .lock
      .log
      .lua
      .md
      .mkd
      .out
      .pl
      .pm
      .png
      .py[oc]*
      .r*html
      .rdoc
      .ri
      .sh
      .sql
      .toml
      .ttf
      .txt
      .xml
      .yml
      Gemfile
      LICENSE
      README
      Rakefile
      VERSION
    ).freeze

    IGNORED_PATTERNS = %w(
      /share/doc/
      /share/postgresql/
      /share/terminfo/
      /terminfo/
    ).freeze

    class << self
      # @see (HealthCheck#new)
      def run!(project)
        new(project).run!
      end
    end

    #
    # The project to healthcheck.
    #
    # @return [Project]
    #
    attr_reader :project

    #
    # Run the healthchecks against the given project. It is assumed that the
    # project has already been built.
    #
    # @param [Project] project
    #   the project to health check
    #
    def initialize(project)
      @project = project
    end

    #
    # Run the given health check. Healthcheks are skipped on Windows.
    #
    # @raise [HealthCheckFailed]
    #   if the health check fails
    #
    # @return [true]
    #   if the healthchecks pass
    #
    def run!
      log.info(log_key) {"Running health on #{project.name}"}
      bad_libs =  case Ohai['platform']
                  when 'mac_os_x'
                    health_check_otool
                  when 'aix'
                    health_check_aix
                  when 'windows'
                    # TODO: objdump -p will provided a very limited check of
                    # explicit dependencies on windows. Most dependencies are
                    # implicit and hence not detected.
                    log.warn(log_key) { 'Skipping dependency health checks on Windows.' }
                    {}
                  else
                    health_check_ldd
                  end

      unresolved = []
      unreliable = []
      detail = []

      if bad_libs.keys.length > 0
        bad_libs.each do |name, lib_hash|
          lib_hash.each do |lib, linked_libs|
            linked_libs.each do |linked, count|
              if linked =~ /not found/
                unresolved << lib unless unresolved.include? lib
              else
                unreliable << linked unless unreliable.include? linked
              end
              detail << "#{name}|#{lib}|#{linked}|#{count}"
            end
          end
        end

        log.error(log_key) { 'Failed!' }
        bad_omnibus_libs, bad_omnibus_bins = bad_libs.keys.partition { |k| k.include? 'embedded/lib' }

        log.error(log_key) do
          out = "The following libraries have unsafe or unmet dependencies:\n"

          bad_omnibus_libs.each do |lib|
            out << "    --> #{lib}\n"
          end

          out
        end

        log.error(log_key) do
          out = "The following binaries have unsafe or unmet dependencies:\n"

          bad_omnibus_bins.each do |bin|
            out << "    --> #{bin}\n"
          end

          out
        end

        if unresolved.length > 0
          log.error(log_key) do
            out = "The following requirements could not be resolved:\n"

            unresolved.each do |lib|
              out << "    --> #{lib}\n"
            end

            out
          end
        end

        if unreliable.length > 0
          log.error(log_key) do
            out =  "The following libraries cannot be guaranteed to be on "
            out << "target systems:\n"

            unreliable.each do |lib|
              out << "    --> #{lib}\n"
            end

            out
          end
        end

        log.error(log_key) do
          out = "The precise failures were:\n"

          detail.each do |line|
            item, dependency, location, count = line.split('|')
            reason = location =~ /not found/ ? 'Unresolved dependency' : 'Unsafe dependency'

            out << "    --> #{item}\n"
            out << "    DEPENDS ON: #{dependency}\n"
            out << "      COUNT: #{count}\n"
            out << "      PROVIDED BY: #{location}\n"
            out << "      FAILED BECAUSE: #{reason}\n"
          end

          out
        end

        raise HealthCheckFailed
      end

      conflict_map = {}

      conflict_map = relocation_check if relocation_checkable?

      if conflict_map.keys.length > 0
        log.warn(log_key) { 'Multiple dlls with overlapping images detected' }

        conflict_map.each do |lib_name, data|
          base = data[:base]
          size = data[:size]
          next_valid_base = data[:base] + data[:size]

          log.warn(log_key) do
            out =  "Overlapping dll detected:\n"
            out << "    #{lib_name} :\n"
            out << "    IMAGE BASE: #{hex}\n" % base
            out << "    IMAGE SIZE: #{hex} (#{size} bytes)\n" % size
            out << "    NEXT VALID BASE: #{hex}\n" % next_valid_base
            out << "    CONFLICTS:\n"

            data[:conflicts].each do |conflict_name|
              cbase = conflict_map[conflict_name][:base]
              csize = conflict_map[conflict_name][:size]
              out << "    - #{conflict_name} #{hex} + #{hex}\n" % [cbase, csize]
            end

            out
          end
        end

        # Don't raise an error yet. This is only bad for FIPS mode.
      end

      true
    end

    # Ensure the method relocation_check is able to run
    #
    # @return [Boolean]
    #
    def relocation_checkable?
      return false unless windows?

      begin
        require 'pedump'
        true
      rescue LoadError
        false
      end
    end

    # Check dll image location overlap/conflicts on windows.
    #
    # @return [Hash<String, Hash<Symbol, ...>>]
    #   library_name ->
    #     :base -> base address
    #     :size -> the total image size in bytes
    #     :conflicts -> array of library names that overlap
    #
    def relocation_check
      conflict_map = {}
<commit_msg>Track the time taken for healthcheck<commit_after>
# Copyright 2012-2014 Chef Software, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

require 'omnibus/sugarable'
begin
  require 'pedump'
rescue LoadError
  STDERR.puts "pedump not found - windows health checks disabled"
end

module Omnibus
  class HealthCheck
    include Instrumentation
    include Logging
    include Util
    include Sugarable

    WHITELIST_LIBS = [
      /ld-linux/,
      /libc\.so/,
      /libcrypt\.so/,
      /libdl/,
      /libfreebl\d\.so/,
      /libgcc_s\.so/,
      /libm\.so/,
      /libnsl\.so/,
      /libpthread/,
      /libresolv\.so/,
      /librt\.so/,
      /libstdc\+\+\.so/,
      /libutil\.so/,
      /linux-vdso.+/,
      /linux-gate\.so/,
    ].freeze

    ARCH_WHITELIST_LIBS = [
      /libc\.so/,
      /libcrypt\.so/,
      /libdb-5\.3\.so/,
      /libdl\.so/,
      /libffi\.so/,
      /libgdbm\.so/,
      /libm\.so/,
      /libnsl\.so/,
      /libpthread\.so/,
      /librt\.so/,
      /libutil\.so/,
    ].freeze

    AIX_WHITELIST_LIBS = [
      /libpthread\.a/,
      /libpthreads\.a/,
      /libdl.a/,
      /librtl\.a/,
      /libc\.a/,
      /libcrypt\.a/,
      /unix$/,
    ].freeze

    SOLARIS_WHITELIST_LIBS = [
      /libaio\.so/,
      /libavl\.so/,
      /libcrypt_[di]\.so/,
      /libcrypto.so/,
      /libcurses\.so/,
      /libdoor\.so/,
      /libgen\.so/,
      /libmd5\.so/,
      /libmd\.so/,
      /libmp\.so/,
      /libresolv\.so/,
      /libscf\.so/,
      /libsec\.so/,
      /libsocket\.so/,
      /libssl.so/,
      /libthread.so/,
      /libuutil\.so/,
      /libkstat\.so/,
      # solaris 11 libraries:
      /libc\.so\.1/,
      /libm\.so\.2/,
      /libdl\.so\.1/,
      /libnsl\.so\.1/,
      /libpthread\.so\.1/,
      /librt\.so\.1/,
      /libcrypt\.so\.1/,
      /libgdbm\.so\.3/,
      /libgcc_s\.so\.1/,
      /libcryptoutil\.so\.1/,
      /libucrypto\.so\.1/,
      /libz\.so\.1/, # while we package our own libz, this get dragged along from Solaris 11's libelf library for some reason...
      /libelf\.so\.1/,
      /libssp\.so\.0/,
      # solaris 9 libraries:
      /libm\.so\.1/,
      /libc_psr\.so\.1/,
      /s9_preload\.so\.1/,
    ].freeze

    SMARTOS_WHITELIST_LIBS = [
      /libm.so/,
      /libpthread.so/,
      /librt.so/,
      /libsocket.so/,
      /libdl.so/,
      /libnsl.so/,
      /libgen.so/,
      /libmp.so/,
      /libmd.so/,
      /libc.so/,
      /libgcc_s.so/,
      /libstdc\+\+\.so/,
      /libcrypt.so/,
    ].freeze

    MAC_WHITELIST_LIBS = [
      /libobjc\.A\.dylib/,
      /libSystem\.B\.dylib/,
      /CoreFoundation/,
      /CoreServices/,
      /Tcl$/,
      /Cocoa$/,
      /Carbon$/,
      /IOKit$/,
      /Tk$/,
      /libutil\.dylib/,
      /libffi\.dylib/,
      /libncurses\.5\.4\.dylib/,
      /libiconv/,
      /libstdc\+\+\.6\.dylib/,
      /libc\+\+\.1\.dylib/,
    ].freeze

    FREEBSD_WHITELIST_LIBS = [
      /libc\.so/,
      /libgcc_s\.so/,
      /libcrypt\.so/,
      /libm\.so/,
      /librt\.so/,
      /libthr\.so/,
      /libutil\.so/,
      /libelf\.so/,
      /libkvm\.so/,
      /libprocstat\.so/,
      /libmd\.so/,
    ].freeze

    IGNORED_ENDINGS = %w(
      .[ch]
      .e*rb
      .gemspec
      .gitignore
      .h*h
      .java
      .js
      .json
      .lock
      .log
      .lua
      .md
      .mkd
      .out
      .pl
      .pm
      .png
      .py[oc]*
      .r*html
      .rdoc
      .ri
      .sh
      .sql
      .toml
      .ttf
      .txt
      .xml
      .yml
      Gemfile
      LICENSE
      README
      Rakefile
      VERSION
    ).freeze

    IGNORED_PATTERNS = %w(
      /share/doc/
      /share/postgresql/
      /share/terminfo/
      /terminfo/
    ).freeze

    class << self
      # @see (HealthCheck#new)
      def run!(project)
        new(project).run!
      end
    end

    #
    # The project to healthcheck.
    #
    # @return [Project]
    #
    attr_reader :project

    #
    # Run the healthchecks against the given project. It is assumed that the
    # project has already been built.
    #
    # @param [Project] project
    #   the project to health check
    #
    def initialize(project)
      @project = project
    end

    #
    # Run the given health check. Healthcheks are skipped on Windows.
    #
    # @raise [HealthCheckFailed]
    #   if the health check fails
    #
    # @return [true]
    #   if the healthchecks pass
    #
    def run!
      measure("Health check time") do
        log.info(log_key) {"Running health on #{project.name}"}
        bad_libs =  case Ohai['platform']
                    when 'mac_os_x'
                      health_check_otool
                    when 'aix'
                      health_check_aix
                    when 'windows'
                      # TODO: objdump -p will provided a very limited check of
                      # explicit dependencies on windows. Most dependencies are
                      # implicit and hence not detected.
                      log.warn(log_key) { 'Skipping dependency health checks on Windows.' }
                      {}
                    else
                      health_check_ldd
                    end

        unresolved = []
        unreliable = []
        detail = []

        if bad_libs.keys.length > 0
          bad_libs.each do |name, lib_hash|
            lib_hash.each do |lib, linked_libs|
              linked_libs.each do |linked, count|
                if linked =~ /not found/
                  unresolved << lib unless unresolved.include? lib
                else
                  unreliable << linked unless unreliable.include? linked
                end
                detail << "#{name}|#{lib}|#{linked}|#{count}"
              end
            end
          end

          log.error(log_key) { 'Failed!' }
          bad_omnibus_libs, bad_omnibus_bins = bad_libs.keys.partition { |k| k.include? 'embedded/lib' }

          log.error(log_key) do
            out = "The following libraries have unsafe or unmet dependencies:\n"

            bad_omnibus_libs.each do |lib|
              out << "    --> #{lib}\n"
            end

            out
          end

          log.error(log_key) do
            out = "The following binaries have unsafe or unmet dependencies:\n"

            bad_omnibus_bins.each do |bin|
              out << "    --> #{bin}\n"
            end

            out
          end

          if unresolved.length > 0
            log.error(log_key) do
              out = "The following requirements could not be resolved:\n"

              unresolved.each do |lib|
                out << "    --> #{lib}\n"
              end

              out
            end
          end

          if unreliable.length > 0
            log.error(log_key) do
              out =  "The following libraries cannot be guaranteed to be on "
              out << "target systems:\n"

              unreliable.each do |lib|
                out << "    --> #{lib}\n"
              end

              out
            end
          end

          log.error(log_key) do
            out = "The precise failures were:\n"

            detail.each do |line|
              item, dependency, location, count = line.split('|')
              reason = location =~ /not found/ ? 'Unresolved dependency' : 'Unsafe dependency'

              out << "    --> #{item}\n"
              out << "    DEPENDS ON: #{dependency}\n"
              out << "      COUNT: #{count}\n"
              out << "      PROVIDED BY: #{location}\n"
              out << "      FAILED BECAUSE: #{reason}\n"
            end

            out
          end

          raise HealthCheckFailed
        end

        conflict_map = {}

        conflict_map = relocation_check if relocation_checkable?

        if conflict_map.keys.length > 0
          log.warn(log_key) { 'Multiple dlls with overlapping images detected' }

          conflict_map.each do |lib_name, data|
            base = data[:base]
            size = data[:size]
            next_valid_base = data[:base] + data[:size]

            log.warn(log_key) do
              out =  "Overlapping dll detected:\n"
              out << "    #{lib_name} :\n"
              out << "    IMAGE BASE: #{hex}\n" % base
              out << "    IMAGE SIZE: #{hex} (#{size} bytes)\n" % size
              out << "    NEXT VALID BASE: #{hex}\n" % next_valid_base
              out << "    CONFLICTS:\n"

              data[:conflicts].each do |conflict_name|
                cbase = conflict_map[conflict_name][:base]
                csize = conflict_map[conflict_name][:size]
                out << "    - #{conflict_name} #{hex} + #{hex}\n" % [cbase, csize]
              end

              out
            end
          end

          # Don't raise an error yet. This is only bad for FIPS mode.
        end

        true
      end
    end

    # Ensure the method relocation_check is able to run
    #
    # @return [Boolean]
    #
    def relocation_checkable?
      return false unless windows?

      begin
        require 'pedump'
        true
      rescue LoadError
        false
      end
    end

    # Check dll image location overlap/conflicts on windows.
    #
    # @return [Hash<String, Hash<Symbol, ...>>]
    #   library_name ->
    #     :base -> base address
    #     :size -> the total image size in bytes
    #     :conflicts -> array of library names that overlap
    #
    def relocation_check
      conflict_map = {}

{'fraction_non_alphanumeric': 0.12812771836463183, 'fraction_numerical': 0.004707567927135036, 'mean_word_length': 1.3280524121500894, 'pattern_counts': {'":': 0, '<': 87, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7291045', 'n_tokens_mistral': 6666, 'n_tokens_neox': 6183, 'n_words': 1496}