This file is indexed.

/usr/lib/ruby/vendor_ruby/chef_zero/endpoints/cookbook_version_endpoint.rb is in chef-zero 4.5.0-2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
require 'ffi_yajl'
require 'chef_zero/endpoints/rest_object_endpoint'
require 'chef_zero/rest_error_response'
require 'chef_zero/chef_data/data_normalizer'
require 'chef_zero/data_store/data_not_found_error'

module ChefZero
  module Endpoints
    # /organizations/ORG/cookbooks/NAME/VERSION
    class CookbookVersionEndpoint < RestObjectEndpoint
      def get(request)
        if request.rest_path[4] == "_latest" || request.rest_path[4] == "latest"
          request.rest_path[4] = latest_version(list_data(request, request.rest_path[0..3]))
        end
        super(request)
      end

      def put(request)
        name = request.rest_path[3]
        version = request.rest_path[4]
        existing_cookbook = get_data(request, request.rest_path, :nil)

        # Honor frozen
        if existing_cookbook
          existing_cookbook_json = FFI_Yajl::Parser.parse(existing_cookbook, :create_additions => false)
          if existing_cookbook_json['frozen?']
            if request.query_params['force'] != "true"
              raise RestErrorResponse.new(409, "The cookbook #{name} at version #{version} is frozen. Use the 'force' option to override.")
            end
            # For some reason, you are forever unable to modify "frozen?" on a frozen cookbook.
            request_body = FFI_Yajl::Parser.parse(request.body, :create_additions => false)
            if !request_body['frozen?']
              request_body['frozen?'] = true
              request.body = FFI_Yajl::Encoder.encode(request_body, :pretty => true)
            end
          end
        end

        # Set the cookbook
        set_data(request, request.rest_path, request.body, :create_dir, :create)

        # If the cookbook was updated, check for deleted files and clean them up
        if existing_cookbook
          missing_checksums = get_checksums(existing_cookbook) - get_checksums(request.body)
          if missing_checksums.size > 0
            hoover_unused_checksums(missing_checksums, request)
          end
        end

        already_json_response(existing_cookbook ? 200 : 201, populate_defaults(request, request.body))
      end

      def delete(request)
        if request.rest_path[4] == "_latest" || request.rest_path[4] == "latest"
          request.rest_path[4] = latest_version(list_data(request, request.rest_path[0..3]))
        end

        deleted_cookbook = get_data(request)

        response = super(request)
        # Last one out turns out the lights: delete /organizations/ORG/cookbooks/NAME if it no longer has versions
        cookbook_path = request.rest_path[0..3]
        if exists_data_dir?(request, cookbook_path) && list_data(request, cookbook_path).size == 0
          delete_data_dir(request, cookbook_path)
        end

        # Hoover deleted files, if they exist
        hoover_unused_checksums(get_checksums(deleted_cookbook), request)
        response
      end

      def get_checksums(cookbook)
        result = []
        FFI_Yajl::Parser.parse(cookbook, :create_additions => false).each_pair do |key, value|
          if value.is_a?(Array)
            value.each do |file|
              if file.is_a?(Hash) && file.has_key?('checksum')
                result << file['checksum']
              end
            end
          end
        end
        result.uniq
      end

      private

      def hoover_unused_checksums(deleted_checksums, request)
        %w(cookbooks cookbook_artifacts).each do |cookbook_type|
          begin
            cookbooks = data_store.list(request.rest_path[0..1] + [cookbook_type])
          rescue ChefZero::DataStore::DataNotFoundError
            # Not all chef versions support cookbook_artifacts
            raise unless cookbook_type == "cookbook_artifacts"
            cookbooks = []
          end
          cookbooks.each do |cookbook_name|
            data_store.list(request.rest_path[0..1] + [cookbook_type, cookbook_name]).each do |version|
              cookbook = data_store.get(request.rest_path[0..1] + [cookbook_type, cookbook_name, version], request)
              deleted_checksums = deleted_checksums - get_checksums(cookbook)
            end
          end
        end
        deleted_checksums.each do |checksum|
          # There can be a race here if multiple cookbooks are uploading.
          # This deals with an exception on delete, but things can still get deleted
          # that shouldn't be.
          begin
            delete_data(request, request.rest_path[0..1] + ['file_store', 'checksums', checksum], :data_store_exceptions)
          rescue ChefZero::DataStore::DataNotFoundError
          end
        end
      end

      def populate_defaults(request, response_json)
        # Inject URIs into each cookbook file
        cookbook = FFI_Yajl::Parser.parse(response_json, :create_additions => false)
        cookbook = ChefData::DataNormalizer.normalize_cookbook(self, request.rest_path[0..1], cookbook, request.rest_path[3], request.rest_path[4], request.base_uri, request.method)
        FFI_Yajl::Encoder.encode(cookbook, :pretty => true)
      end

      def latest_version(versions)
        sorted = versions.sort_by { |version| Gem::Version.new(version.dup) }
        sorted[-1]
      end
    end
  end
end