CVE-2012-3864 (Ruby, published 2012-08-06T16:55Z)
Description: value: Puppet before 2.6.17 and 2.7.x before 2.7.18, and Puppet Enterprise before 2.5.2, allows remote authenticated users to read arbitrary files on the puppet master server by leveraging an arbitrary user's certificate and private key in a GET request.
Severity: MEDIUM (CVSS v3 base score: )
Weakness: CWE-200 \u2014 Exposure of Sensitive Information to an Unauthorized Actor
The product exposes sensitive information to an actor that is not explicitly authorized to have access to that information.
Repository: https://github.com/puppetlabs/puppet
Files changed:
lib/puppet/application/master.rb
lib/puppet/file_serving/configuration.rb
lib/puppet/file_serving/content.rb
lib/puppet/file_serving/metadata.rb
lib/puppet/file_serving/terminus_selector.rb
lib/puppet/indirector/file_content/selector.rb
lib/puppet/indirector/file_metadata/selector.rb
spec/shared_behaviours/file_serving.rb
spec/unit/file_serving/content_spec.rb
spec/unit/file_serving/metadata_spec.rb
spec/unit/file_serving/terminus_selector_spec.rb
Commit message: Add Selector terminus for file_content/file_metadata

This terminus is now the default, and encapsulates the behavior that was
previously in the IndirectionHooks. That is, we dynamically select the
terminus to use for a file request based on the key. However, for the
puppet master, we instead explicitly always use the FileServer terminus,
so that *all* requests for files from the master will go through
the file server. This ensures that we will never accidentally
serve local files on the puppet master.


Vulnerable code:
    raise(ArgumentError, "Cannot find file: Invalid path '#{mount_name}'") unless mount_name =~ %r{^[-\w]+$}
require 'puppet/file_serving/indirection_hooks'
  indirects :file_content, :extend => Puppet::FileServing::IndirectionHooks
require 'puppet/file_serving/indirection_hooks'
  indirects :file_metadata, :extend => Puppet::FileServing::IndirectionHooks
require 'uri'
require 'puppet/util'
module Puppet::FileServing::IndirectionHooks
  # Pick an appropriate terminus based on the protocol.
  def select_terminus(request)
    term = @indirection.terminus(:rest)
    @indirection.stubs(:terminus).with(:rest).returns term
    term.expects(:find)
  it "should should be a subclass of Base" do
  it "should should include the IndirectionHooks module in its indirection" do
    Puppet::FileServing::Content.indirection.singleton_class.included_modules.should include(Puppet::FileServing::IndirectionHooks)
  end

  it "should should be a subclass of Base" do
  it "should should include the IndirectionHooks module in its indirection" do
    Puppet::FileServing::Metadata.indirection.singleton_class.included_modules.should include(Puppet::FileServing::IndirectionHooks)
  end

require 'puppet/file_serving/indirection_hooks'
describe Puppet::FileServing::IndirectionHooks do
    @object.extend(Puppet::FileServing::IndirectionHooks)
      @object.select_terminus(@request).should == :file
      @object.select_terminus(@request).should == :file
      proc { @object.select_terminus(@request) }.should raise_error(ArgumentError)
        @object.select_terminus(@request).should == :rest
        @request.stubs(:server).returns "foo"
        @object.select_terminus(@request).should == :rest
        Puppet.settings.expects(:value).with(:name).returns "puppet"
        @object.select_terminus(@request).should == :file_server

Fixed code:
    Puppet::FileServing::Content.indirection.terminus_class = :file_server
    Puppet::FileServing::Metadata.indirection.terminus_class = :file_server

    raise(ArgumentError, "Cannot find file: Invalid mount '#{mount_name}'") unless mount_name =~ %r{^[-\w]+$}
  indirects :file_content, :terminus_class => :selector
  indirects :file_metadata, :terminus_class => :selector
module Puppet::FileServing::TerminusSelector
  def select(request)
require 'puppet/indirector/file_content'
require 'puppet/indirector/code'
require 'puppet/file_serving/terminus_selector'

class Puppet::Indirector::FileContent::Selector < Puppet::Indirector::Code
  desc "Select the terminus based on the request"
  include Puppet::FileServing::TerminusSelector

  def get_terminus(request)
    indirection.terminus(select(request))
  end

  def find(request)
    get_terminus(request).find(request)
  end

  def search(request)
    get_terminus(request).search(request)
  end

  def authorized?(request)
    terminus = get_terminus(request)
    if terminus.respond_to?(:authorized?)
      terminus.authorized?(request)
    else
      true
    end
  end
end
require 'puppet/indirector/file_metadata'
require 'puppet/indirector/code'
require 'puppet/file_serving/terminus_selector'

class Puppet::Indirector::FileMetadata::Selector < Puppet::Indirector::Code
  desc "Select the terminus based on the request"
  include Puppet::FileServing::TerminusSelector

  def get_terminus(request)
    indirection.terminus(select(request))
  end

  def find(request)
    get_terminus(request).find(request)
  end

  def search(request)
    get_terminus(request).search(request)
  end

  def authorized?(request)
    terminus = get_terminus(request)
    if terminus.respond_to?(:authorized?)
      terminus.authorized?(request)
    else
      true
    end
  end
end
    @indirection.terminus(:rest).expects(:find)
  it "should be a subclass of Base" do
  it "should be a subclass of Base" do
require 'puppet/file_serving/terminus_selector'
describe Puppet::FileServing::TerminusSelector do
    @object.extend(Puppet::FileServing::TerminusSelector)
      @object.select(@request).should == :file
      @object.select(@request).should == :file
      proc { @object.select(@request) }.should raise_error(ArgumentError)
        @object.select(@request).should == :rest
        # We have to stub this because we can't set name
        @object.select(@request).should == :rest
        @object.select(@request).should == :file_server