test_name "CVE 2013-4761 Injection of bad class names causing code loading" do
  confine :except, :platform => 'windows'

  tag 'audit:high',        # low risk, high (security) impact
      'audit:integration', # issue is completely on the master side
      'server'

  testdir = create_tmpdir_for_user master, 'class-names-injection'
  exploit_path = "#{testdir}/exploit.rb"
  exploited_path = "#{testdir}/exploited"

  # @return [String] path to the manifest file
  def create_exploit_manifest(path, exploit_path_expression)
    apply_manifest_on(master, <<-MANIFEST, :catch_failures => true)
      File {
        ensure => directory,
        mode => "0770",
        owner => #{master.puppet['user']},
        group => #{master.puppet['group']},
      }

      file {
        '#{path}/environments':;
        '#{path}/environments/production':;
        '#{path}/environments/production/manifests':;
        '#{path}/environments/production/manifests/site.pp':
          ensure => file,
          content => '
$enc_data = "#{exploit_path_expression}"
include $enc_data
',
          mode => "0640",
      }
    MANIFEST
  end

  def should_not_be_able_to_exploit(exploited_path)
    agents.each do |agent|
      next if agent['roles'].include?('master')

      step "Request a catalog to trigger the exploit" do
        on agent, puppet('agent', '-t'), :acceptable_exit_codes => [1]
      end

      step "Check that the exploit marker was not created" do
        on master, "test ! -e #{exploited_path}"
      end
    end
  end

  step "Create exploit file" do
    create_remote_file(master, exploit_path, <<-EXPLOIT)
    ::File.open('#{exploited_path}', 'w') { |f| f.puts("exploited") }
    EXPLOIT

    on(master, "chmod 777 #{exploit_path}")
  end

  master_opts = {
    'main' => {
      'environmentpath' => "#{testdir}/environments",
    },
  }

  with_puppet_running_on(master, master_opts) do
    step "Class name is not interpreted as an absolute path" do
      create_exploit_manifest(testdir, 'tmp::exploit')
      should_not_be_able_to_exploit(exploited_path)
    end

    step "Class name cannot be used for a directory traversal out of the module path" do
      # This is just a guess about how far back we need to go...
      traversal_exploit_expression = "#{'::..' * 20}#{exploit_path.gsub(File::SEPARATOR,'::')}"
      create_exploit_manifest(testdir, traversal_exploit_expression)

      should_not_be_able_to_exploit(exploited_path)
    end
  end
end
