VYPR
Moderate severityNVD Advisory· Published Aug 22, 2018· Updated Aug 5, 2024

CVE-2017-2662

CVE-2017-2662

Description

A flaw was found in Foreman's katello plugin version 3.4.5. After setting a new role to allow restricted access on a repository with a filter (filter set on the Product Name), the filter is not respected when the actions are done via hammer using the repository id.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

A flaw in Foreman's katello plugin 3.4.5 fails to enforce repository filters when actions are performed via hammer using the repository ID.

Vulnerability

A flaw was found in Foreman's Katello plugin version 3.4.5 [1]. When a role is configured to restrict access to a repository with a filter set on the Product Name, the filter is not respected if actions are performed via the hammer CLI using the repository ID instead of the product name [1]. This bypass allows users with restricted roles to interact with repositories they should not have access to.

Exploitation

An attacker with a restricted role that includes a filter on Product Name can bypass the intended access control by using the hammer CLI and specifying the repository ID directly [1]. No additional authentication or privileges are required beyond having a restricted role that normally would limit access via product name filters. The hammer commands that use repository IDs will succeed even when the user lacks the necessary permissions for that repository.

Impact

Successful exploitation allows an attacker to perform actions (such as viewing or managing content) on repositories that should be restricted based on the filter [1]. This leads to unauthorized access to sensitive content, potentially resulting in information disclosure or further compromise of the system.

Mitigation

The issue has been addressed in the Katello codebase via commit [2] and pull request [4]. Users should update to a version of the Katello plugin that includes the fix. No workaround is mentioned in the references, but restricting hammer CLI access or using product names instead of repository IDs may reduce risk. If no patch is applied, the vulnerability remains exploitable.

AI Insight generated on May 22, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
katelloRubyGems
< 3.17.0.rc13.17.0.rc1

Affected products

2
  • ghsa-coords
    Range: < 3.17.0.rc1
  • The Foreman Project/foreman katello pluginv5
    Range: 3.4.5

Patches

1
853260e3e9f9

Fixes #18035 - filter repos for CV with perms

https://github.com/Katello/katelloJustin SherrillJun 15, 2020via ghsa
5 files changed · +193 1
  • app/controllers/katello/api/v2/api_controller.rb+1 0 modified
    @@ -5,6 +5,7 @@ class Api::V2::ApiController < ::Api::V2::BaseController
         include Api::V2::Rendering
         include Api::V2::ErrorHandling
         include ::Foreman::Controller::CsvResponder
    +    include Concerns::Api::V2::AssociationsPermissionCheck
     
         # support for session (thread-local) variables must be the last filter in this class
         include Foreman::ThreadSession::Cleaner
    
  • app/controllers/katello/api/v2/content_views_controller.rb+7 0 modified
    @@ -22,6 +22,13 @@ class Api::V2::ContentViewsController < Api::V2::ApiController
           param :solve_dependencies, :bool, :desc => N_("Solve RPM dependencies by default on Content View publish, defaults to false")
         end
     
    +    def filtered_associations
    +      {
    +        :component_ids => Katello::ContentViewVersion,
    +        :repository_ids => Katello::Repository
    +      }
    +    end
    +
         api :GET, "/organizations/:organization_id/content_views", N_("List content views")
         api :GET, "/content_views", N_("List content views")
         param :organization_id, :number, :desc => N_("organization identifier")
    
  • app/controllers/katello/concerns/api/v2/associations_permission_check.rb+67 0 added
    @@ -0,0 +1,67 @@
    +module Katello
    +  module Concerns
    +    module Api::V2::AssociationsPermissionCheck
    +      extend ActiveSupport::Concern
    +
    +      # The purpose of this module is to protect a controller from a user creating or updating some association
    +      # when they do not have permissions to view the associated items.  An example would be adding random repository ids to
    +      # content view.
    +      # To support this, within the controller define a method such as:
    +      #     def filtered_associations
    +      #       {
    +      #         :component_ids => Katello::ContentViewVersion,
    +      #         :repository_ids => Katello::Repository
    +      #       }
    +      #     end
    +      # This assumes that the parameters are 'wrapped'.  So the above in the content_views_controller, actually looks at
    +      #  a subhash of 'content_view'
    +
    +      included do
    +        before_action :check_association_ids, :only => [:create, :update]
    +      end
    +
    +      def check_association_ids
    +        if filtered_associations
    +          wrapped_params = params[self._wrapper_options.name]
    +          find_param_arrays(wrapped_params).each do |key_path|
    +            if (model_class = filtered_associations.with_indifferent_access.dig(*key_path))
    +              param_ids = wrapped_params.dig(*key_path)
    +              filtered_ids = model_class.readable.where(:id => param_ids).pluck(:id)
    +              if (unfound_ids = param_ids_missing(param_ids, filtered_ids)).any?
    +                fail HttpErrors::NotFound, _("One or more ids (%{ids}) were not found for %{assoc}.  You may not have permissions to see them.") %
    +                    {ids: unfound_ids, assoc: key_path.last}
    +              end
    +            else
    +              fail _("Unfiltered params array: %s.") % key_path
    +            end
    +          end
    +        else
    +          Rails.logger.warn("#{self.class.name} may has unprotected associations, see associations_permission_check.rb for details.") if ENV['RAILS_ENV'] == 'development'
    +        end
    +      end
    +
    +      def filtered_associations
    +        #should return {} when supported by all controllers
    +        nil
    +      end
    +
    +      def param_ids_missing(param_ids, filtered_ids)
    +        param_ids.map(&:to_i).uniq - filtered_ids.map(&:to_i).uniq
    +      end
    +
    +      #returns an array of list of keys pointing to an array in a params hash i.e.:
    +      # {"a"=> {"b" => [3]}}  =>  [["a", "b"]]
    +      def find_param_arrays(hash = params)
    +        list_of_paths = []
    +        hash.each do |key, value|
    +          if value.is_a?(ActionController::Parameters) || value.is_a?(Hash)
    +            list_of_paths += find_param_arrays(value).compact.map { |inner_keys| [key] + inner_keys }
    +          elsif value.is_a?(Array)
    +            list_of_paths << [key]
    +          end
    +        end
    +        list_of_paths.compact
    +      end
    +    end
    +  end
    +end
    
  • test/controllers/api/v2/concerns/associations_permission_check_test.rb+78 0 added
    @@ -0,0 +1,78 @@
    +# encoding: utf-8
    +
    +require "katello_test_helper"
    +
    +module Katello
    +  class TestAssociationIdController
    +    def initialize(params, filtered_associations)
    +      @params = params
    +      @filtered_associations = filtered_associations
    +    end
    +
    +    def self.before_action(*_args)
    +    end
    +
    +    include Concerns::Api::V2::AssociationsPermissionCheck
    +
    +    attr_accessor :filtered_associations
    +    attr_reader :params
    +
    +    def _wrapper_options
    +      OpenStruct.new(:name => :content_view)
    +    end
    +  end
    +
    +  class Api::V2::AssociationsPermissionCheckTest < ActiveSupport::TestCase
    +    def setup
    +      @cv = katello_content_views(:acme_default)
    +      @repo = katello_repositories(:fedora_17_x86_64)
    +
    +      @params = {
    +        content_view: {
    +          foo: [@cv.id],
    +          foo2: 3,
    +          foo3: {
    +            baz: [@repo.id],
    +            baz2: 9
    +          }
    +        }
    +      }
    +
    +      @filtered_associations = {
    +        foo: ::Katello::ContentView,
    +        foo3: {
    +          baz: ::Katello::Repository
    +        }
    +      }
    +    end
    +
    +    def test_find_param_arrays
    +      controller = TestAssociationIdController.new(@params, @filtered_associations)
    +      assert_equal [[:content_view, :foo], [:content_view, :foo3, :baz]].sort, controller.find_param_arrays.sort
    +    end
    +
    +    def test_check_association_ids_positive
    +      controller = TestAssociationIdController.new(@params, @filtered_associations)
    +
    +      controller.check_association_ids
    +    end
    +
    +    def test_check_association_ids_not_found_id
    +      @params[:content_view][:foo] << -1
    +      controller = TestAssociationIdController.new(@params, @filtered_associations)
    +
    +      assert_raises(Katello::HttpErrors::NotFound) do
    +        controller.check_association_ids
    +      end
    +    end
    +
    +    def test_check_association_ids_not_defined
    +      @params[:content_view][:not_defined] = [1]
    +      controller = TestAssociationIdController.new(@params, @filtered_associations)
    +
    +      assert_raises(StandardError) do
    +        controller.check_association_ids
    +      end
    +    end
    +  end
    +end
    
  • test/controllers/api/v2/content_views_controller_test.rb+40 1 modified
    @@ -125,6 +125,30 @@ def test_create_protected
           end
         end
     
    +    def test_create_protected_without_repo_read
    +      user = User.unscoped.find(users(:restricted).id)
    +      denied_perms = [@create_permission, @view_permission, @update_permission, :destroy_content_views]
    +      setup_user_with_permissions(denied_perms, user)
    +      repository = katello_repositories(:fedora_17_unpublished)
    +      login_user(user)
    +
    +      post :create, params: {:organization_id => @organization.id, content_view: { :name => "Test", :repository_ids => [repository.id] } }
    +
    +      assert_response 404
    +    end
    +
    +    def test_create_protected_without_repo_read_non_wrapped
    +      user = User.unscoped.find(users(:restricted).id)
    +      denied_perms = [@create_permission, @view_permission, @update_permission, :destroy_content_views]
    +      setup_user_with_permissions(denied_perms, user)
    +      repository = katello_repositories(:fedora_17_unpublished)
    +
    +      login_user(user)
    +      post :create, params: { :name => "Test", :organization_id => @organization.id, :repository_ids => [repository.id] }
    +
    +      assert_response 404
    +    end
    +
         def test_create_with_non_json_request
           @request.env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'
           post :create, params: { :name => "My View", :description => "Cool", :organization_id => @organization.id }
    @@ -175,6 +199,21 @@ def test_update
         def test_update_repositories
           repository = katello_repositories(:fedora_17_unpublished)
     
    +      params = { :repository_ids => [repository.id] }
    +      assert_sync_task(::Actions::Katello::ContentView::Update) do |_content_view, content_view_params|
    +        content_view_params.key?(:repository_ids).must_equal true
    +        content_view_params[:repository_ids].must_equal params[:repository_ids]
    +      end
    +      put :update, params: { :id => @library_dev_staging_view.id, :content_view => params }
    +
    +      assert_response :success
    +      assert_template layout: 'katello/api/v2/layouts/resource'
    +      assert_template 'katello/api/v2/common/update'
    +    end
    +
    +    def test_update_repositories_strings
    +      repository = katello_repositories(:fedora_17_unpublished)
    +
           params = { :repository_ids => [repository.id.to_s] }
           assert_sync_task(::Actions::Katello::ContentView::Update) do |_content_view, content_view_params|
             content_view_params.key?(:repository_ids).must_equal true
    @@ -191,7 +230,7 @@ def test_update_components
           version = @library_dev_staging_view.versions.first
           composite = ContentView.find(katello_content_views(:composite_view).id)
     
    -      params = { :component_ids => [version.id.to_s] }
    +      params = { :component_ids => [version.id] }
           assert_sync_task(::Actions::Katello::ContentView::Update) do |_content_view, content_view_params|
             content_view_params.key?(:component_ids).must_equal true
             content_view_params[:component_ids].must_equal params[:component_ids]
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.