diff -Nru puppet-2.7.11/debian/changelog puppet-2.7.11/debian/changelog --- puppet-2.7.11/debian/changelog 2012-07-10 12:06:00.000000000 +0000 +++ puppet-2.7.11/debian/changelog 2013-03-11 16:32:32.000000000 +0000 @@ -1,3 +1,17 @@ +puppet (2.7.11-1ubuntu2.2) precise-security; urgency=low + + * SECURITY UPDATE: Multiple security issues + - debian/patches/security-mar-2013.patch: upstream patch to fix + multiple security issues. + - CVE-2013-1640 - Remote code execution on master from authenticated clients + - CVE-2013-1652 - Insufficient input validation + - CVE-2013-1653 - Remote code execution + - CVE-2013-1654 - Protocol downgrade + - CVE-2013-1655 - Unauthenticated remote code execution risk + - CVE-2013-2275 - Incorrect default report ACL + + -- Marc Deslauriers Mon, 11 Mar 2013 12:32:11 -0400 + puppet (2.7.11-1ubuntu2.1) precise-security; urgency=low * SECURITY UPDATE: Multiple July 2012 security issues diff -Nru puppet-2.7.11/debian/patches/security-mar-2013.patch puppet-2.7.11/debian/patches/security-mar-2013.patch --- puppet-2.7.11/debian/patches/security-mar-2013.patch 1970-01-01 00:00:00.000000000 +0000 +++ puppet-2.7.11/debian/patches/security-mar-2013.patch 2013-03-12 15:10:08.000000000 +0000 @@ -0,0 +1,2488 @@ +Index: puppet-2.7.11/acceptance/tests/security/cve-2013-1640_facter_string.rb +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ puppet-2.7.11/acceptance/tests/security/cve-2013-1640_facter_string.rb 2013-03-11 12:32:08.851427540 -0400 +@@ -0,0 +1,14 @@ ++# Setting a custom fact to "string" will overwrite a local variable during ++# template compilation on the master allowing remote code execution by ++# any authenticated client. ++test_name "CVE 2013-1640 Remote Code Execution" do ++ confine :except, :platform => 'windows' ++ ++ on agents, %q[ FACTER_string="<%= %x{ /bin/echo hax0rd } %>" ] + ++ %q[ puppet apply -e ] + ++ %q[ 'notice(inline_template("<%= \"I am Safe\" %>"))' ] do |test| ++ ++ assert_match /I am Safe/, test.stdout ++ assert_no_match /hax0rd/, test.stdout ++ end ++end +Index: puppet-2.7.11/acceptance/tests/security/cve-2013-1652_improper_query_params.rb +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ puppet-2.7.11/acceptance/tests/security/cve-2013-1652_improper_query_params.rb 2013-03-11 12:32:08.851427540 -0400 +@@ -0,0 +1,39 @@ ++require 'json' ++ ++test_name "CVE 2013-1652 Improper query parameter validation" do ++ confine :except, :platform => 'windows' ++ ++ with_master_running_on( master, '--autosign true' ) do ++ # Ensure each agent has a signed cert ++ on agents, puppet_agent( '-t' ) ++ ++ agents.each do |agent| ++ next if agent['roles'].include?( 'master' ) ++ ++ certname = on(agent, puppet_agent("--configprint certname")).stdout.chomp ++ ++ payload = "https://#{master}:8140/production/catalog/#{certname}?use_node=" + ++ "---%20!ruby/object:Puppet::Node%0A%20%20" + ++ "name:%20#{master}%0A%20%20classes:%20\[\]%0A%20%20" + ++ "parameters:%20%7B%7D%0A%20%20facts:%20%7B%7D" ++ ++ cert_path = on(agent, puppet_agent("--configprint hostcert")).stdout.chomp ++ key_path = on(agent, puppet_agent("--configprint hostprivkey")).stdout.chomp ++ curl_base = "curl -g --cert \"#{cert_path}\" --key \"#{key_path}\" -k -H 'Accept: pson'" ++ ++ curl_call = "#{curl_base} '#{payload}'" ++ ++ step "Attempt to retrieve another nodes catalog" do ++ on agent, curl_call do |test| ++ begin ++ res = JSON.parse( test.stdout ) ++ fail_test( "Retrieved catalog for #{master} from #{agent}" ) if ++ res['data']['name'] == master.name ++ rescue JSON::ParserError ++ # good, continue ++ end ++ end ++ end ++ end ++ end ++end +Index: puppet-2.7.11/acceptance/tests/security/cve-2013-1652_poison_other_node_cache.rb +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ puppet-2.7.11/acceptance/tests/security/cve-2013-1652_poison_other_node_cache.rb 2013-03-11 12:32:08.851427540 -0400 +@@ -0,0 +1,40 @@ ++test_name "CVE 2013-1652 Poison node cache" do ++ ++ step "Determine suitability of the test" do ++ versions = on( hosts, puppet( '--version' )) ++ skip_test( "This test will only run on Puppet 3.x" ) if ++ versions.any? {|r| r.stdout =~ /\A2\./ } ++ end ++ ++ with_master_running_on( master, "--autosign true" ) do ++ # Ensure each agent has a signed cert ++ on agents, puppet_agent( '-t' ) ++ ++ agents.each do |agent| ++ next if agent['roles'].include?( 'master' ) ++ ++ certname = on(agent, puppet_agent("--configprint certname")).stdout.chomp ++ cert_path = on(agent, puppet_agent("--configprint hostcert")).stdout.chomp ++ key_path = on(agent, puppet_agent("--configprint hostprivkey")).stdout.chomp ++ ++ curl_base = "curl -g --cert #{cert_path} --key #{key_path} -k -H 'Accept: pson'" ++ ++ step "Attempt to poison the master's node cache" do ++ yamldir = on( master, puppet_master( '--configprint yamldir' )).stdout.chomp ++ exploited = "#{yamldir}/node/you.lose.yaml" ++ on master, "rm -rf #{exploited}" ++ on master, "rm -rf #{yamldir}/node/*" ++ payload2 = "https://#{master}:8140/production/node/#{certname}?instance=" + ++ "---+%21ruby%2Fobject%3APuppet%3A%3ANode%0A+classes" + ++ "%3A%0A+-+foo%0A+name%3A+you.lose%0A+parameters" + ++ "%3A+%7B%7D%0A+time%3A+2013-02-28+15%3A12%3A30.367008+-08%3A00" ++ ++ on agent, "#{curl_base} '#{payload2}'" ++ ++ fail_test( "Found exploit file #{exploited}" ) if ++ on( master, "[ ! -f #{exploited} ]", ++ :acceptable_exit_codes => [0,1] ).exit_code == 1 ++ end ++ end ++ end ++end +Index: puppet-2.7.11/acceptance/tests/security/cve-2013-1653_puppet_kick.rb +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ puppet-2.7.11/acceptance/tests/security/cve-2013-1653_puppet_kick.rb 2013-03-11 12:32:08.851427540 -0400 +@@ -0,0 +1,109 @@ ++test_name "CVE 2013-1653: Puppet Kick Remote Code Exploit" do ++ ++ step "Determine suitability of the test" do ++ confine :except, :platform => 'windows' ++ ++ versions = on( hosts, puppet( '--version' )) ++ skip_test( "This test will not run on Puppet 2.6" ) if ++ versions.any? {|r| r.stdout =~ /\A2\.6\./ } ++ end ++ ++ with_master_running_on( master, '--autosign true' ) do ++ on agents, puppet_agent( '-t' ) ++ end ++ ++ def exploit_code( exploiter, exploitee, endpoint, port, file_to_create ) ++ ++ certfile = on( exploiter, puppet_agent( '--configprint hostcert' )).stdout.chomp ++ keyfile = on( exploiter, puppet_agent( '--configprint hostprivkey' )).stdout.chomp ++ ++ exploit = %Q[#!/usr/bin/env ruby ++ require 'puppet' ++ require 'openssl' ++ require 'net/https' ++ ++ yaml = < 'text/yaml', 'Accept' => 'yaml'} ++ conn = Net::HTTP.new('#{exploitee}', #{port}) ++ conn.use_ssl = true ++ conn.cert = OpenSSL::X509::Certificate.new(File.read('#{certfile}')) ++ conn.key = OpenSSL::PKey::RSA.new(File.read('#{keyfile}')) ++ conn.verify_mode = OpenSSL::SSL::VERIFY_NONE ++ ++ conn.request_put("/production/#{endpoint}/#{exploiter}", yaml, headers) do |response| ++ response.read_body do |chunk| ++ puts chunk ++ end ++ end ] ++ ++ return exploit ++ end ++ ++ exploited = '/tmp/cve-2013-1653-has-worked' ++ restauth_conf = %q[ ++path /run ++auth yes ++allow * ++] ++ ++ teardown do ++ agents.each do |agent| ++ pidfile = on( agent, puppet_agent("--configprint pidfile") ).stdout.chomp ++ on agent, "[ -f #{pidfile} ] && kill `cat #{pidfile}` || true" ++ on agent, "rm -rf #{exploited}" ++ end ++ end ++ ++ agents.each do |agent| ++ atestdir = agent.tmpdir('puppet-kick-auth') ++ mtestdir = master.tmpdir('puppet-kick-auth') ++ ++ step "Daemonize the agent" do ++ # Lay down a tempory auth.conf that will allow the agent to be kicked ++ create_remote_file(agent, "#{atestdir}/auth.conf", restauth_conf) ++ ++ # Start the agent ++ on(agent, puppet_agent("--debug --daemonize --server #{master} --listen --no-client --rest_authconfig #{atestdir}/auth.conf")) ++ ++ step "Wait for agent to start listening" do ++ timeout = 15 ++ begin ++ Timeout.timeout(timeout) do ++ loop do ++ # 7 is "Could not connect to host", which will happen before it's running ++ result = on(agent, "curl -k https://#{agent}:8139", :acceptable_exit_codes => [0,7]) ++ break if result.exit_code == 0 ++ sleep 1 ++ end ++ end ++ rescue Timeout::Error ++ fail_test "Puppet agent #{agent} failed to start after #{timeout} seconds" ++ end ++ end ++ end ++ ++ step "Attempt to exploit #{agent}" do ++ # Ensure there's no stale data ++ on agent, "rm -rf #{exploited}" ++ on master, "rm -rf #{mtestdir}/exploit.rb" ++ ++ # Copy over our exploit and execute ++ create_remote_file( master, "#{mtestdir}/exploit.rb", exploit_code( master, agent, 'run', 8139, exploited )) ++ on master, "chmod +x #{mtestdir}/exploit.rb" ++ on master, "#{mtestdir}/exploit.rb" ++ ++ # Did it work? ++ fail_test( "Found exploit file #{exploited}" ) if ++ on( agent, "[ ! -f #{exploited} ]", ++ :acceptable_exit_codes => [0,1] ).exit_code == 1 ++ end ++ end ++end +Index: puppet-2.7.11/acceptance/tests/security/cve-2013-1654_sslv2_downgrade_agent.rb +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ puppet-2.7.11/acceptance/tests/security/cve-2013-1654_sslv2_downgrade_agent.rb 2013-03-11 12:32:08.851427540 -0400 +@@ -0,0 +1,93 @@ ++test_name "CVE 2013-1654 SSL2 Downgrade of Agent connection" do ++ ++ def which_ruby(host) ++ host['puppetbindir'] ? "#{host['puppetbindir']}/ruby" : 'ruby' ++ end ++ ++ def suitable?(host) ++ cmd = </dev/null &" ++ ++ timeout = 15 ++ begin ++ Timeout.timeout(timeout) do ++ loop do ++ # 7 is "Could not connect to host", which will happen before it's running ++ result = on(agent, "curl -m1 -k https://#{agent}:#{port}", :acceptable_exit_codes => [0,7,35]) ++ break if result.exit_code == 0 or result.exit_code == 35 ++ sleep 1 ++ end ++ end ++ rescue Timeout::Error ++ fail_test "Insecure Mock Server on #{agent} failed to start after #{timeout} seconds" ++ end ++ ++ on(agent, puppet("agent --debug --test --server #{agent} --masterport #{port}"), :acceptable_exit_codes => [1]) do |test| ++ assert_no_match(/'FOOBAR'/, test.stdout) ++ end ++ else ++ logger.debug( "skipping #{agent} since SSLv2 is not available" ) ++ end ++ end ++end +Index: puppet-2.7.11/acceptance/tests/security/cve-2013-1654_sslv2_downgrade_master.rb +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ puppet-2.7.11/acceptance/tests/security/cve-2013-1654_sslv2_downgrade_master.rb 2013-03-11 12:32:08.851427540 -0400 +@@ -0,0 +1,32 @@ ++test_name "CVE 2013-1654 SSL2 Downgrade of Master connection" do ++ ++ def suitable?(host) ++ ruby = host['puppetbindir'] ? "#{host['puppetbindir']}/ruby" : 'ruby' ++ cmd = < (0..255)) do |test| ++ assert_match /CLIENT-HELLO/, test.stdout ++ assert_no_match /SERVER-HELLO/, test.stdout ++ end ++ end ++ else ++ logger.debug( "Not testing master as SSLv2 isn't available to it" ) ++ end ++end +Index: puppet-2.7.11/acceptance/tests/security/cve-2013-1655_safe_yaml_deserialization.rb +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ puppet-2.7.11/acceptance/tests/security/cve-2013-1655_safe_yaml_deserialization.rb 2013-03-11 12:32:08.855427540 -0400 +@@ -0,0 +1,42 @@ ++test_name "#19393: Safe YAML deserialization" ++step "Verify Puppet safely deserializes YAML encoded objects" ++ ++# Check if the master is running with a Psych YAML engine. If not, don't test. ++check_for_psych_cmd = "ruby -ryaml -e 'exit (defined?(YAML::ENGINE) and YAML::ENGINE.yamler == \"psych\") ? 3 : 7'" ++ ++master_uses_psych = 'unknown' ++on master, check_for_psych_cmd, :acceptable_exit_codes => [3,7] do |result| ++ case result.exit_code ++ when 3 ++ master_uses_psych = true ++ when 7 ++ master_uses_psych = false ++ else ++ raise "Could not determine if the system under test uses the YAML Psych engine." ++ end ++end ++ ++if master_uses_psych ++ with_master_running_on(master) do ++ unsafe_data = "--- !ruby/hash:Array {}" ++ ++ cmd = [ ++ "curl -k -X PUT", ++ "--cacert \"$(puppet master --configprint cacert)\"", ++ "--cert \"$(puppet master --configprint hostcert)\"", ++ "--key \"$(puppet master --configprint hostprivkey)\"", ++ "-H 'Content-Type: text/yaml'", ++ "-d '#{unsafe_data}'", ++ "\"https://#{master}:8140/production/report/$(puppet master --configprint certname)\"" ++ ].join(" ") ++ ++ on master, cmd, :acceptable_exit_codes => [0] do ++ msg = "(#19393) (CVE-2013-1655) Puppet master accepted illegal YAML, " + ++ "expected rejection with message 'Illegal YAML mapping found ... " + ++ "please use ... instead'" ++ assert_match(/Illegal YAML mapping found/, stdout, msg) ++ end ++ end ++else ++ skip_test "Cannot validate CVE-2013-1655 unless the master is running with the Psych YAML engine" ++end +Index: puppet-2.7.11/acceptance/tests/security/cve-2013-2274_all_your_agent_terminii_belong_to_us.rb +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ puppet-2.7.11/acceptance/tests/security/cve-2013-2274_all_your_agent_terminii_belong_to_us.rb 2013-03-11 12:32:08.855427540 -0400 +@@ -0,0 +1,104 @@ ++test_name "CVE 2013-2274 Agent terminii" do ++ ++ step "Determine suitability of the test" do ++ confine :except, :platform => 'windows' ++ ++ versions = on( hosts, puppet( '--version' )) ++ skip_test( "This test will only run on Puppet 2.6" ) unless ++ versions.any? {|r| r.stdout =~ /\A2\.6\./ } ++ end ++ ++ with_master_running_on( master, '--autosign true' ) do ++ on agents, puppet_agent( '-t' ) ++ end ++ ++ def exploit_code( exploiter, exploitee, endpoint, port, file_to_create ) ++ ++ certfile = on( exploiter, puppet_agent( '--configprint hostcert' )).stdout.chomp ++ keyfile = on( exploiter, puppet_agent( '--configprint hostprivkey' )).stdout.chomp ++ certname = on( exploiter, puppet_agent( '--configprint certname' )).stdout.chomp ++ ++ exploit = %Q[#!/usr/bin/env ruby ++ require 'puppet' ++ require 'openssl' ++ require 'net/https' ++ ++ exec = Puppet::Type.type(:exec).new(:name => '/bin/touch #{file_to_create}', :logoutput => true) ++ yaml = exec.to_resource.to_yaml ++ ++ headers = {'Content-Type' => 'text/yaml', 'Accept' => 'yaml'} ++ conn = Net::HTTP.new('#{exploitee}', #{port}) ++ conn.use_ssl = true ++ conn.cert = OpenSSL::X509::Certificate.new(File.read('#{certfile}')) ++ conn.key = OpenSSL::PKey::RSA.new(File.read('#{keyfile}')) ++ conn.verify_mode = OpenSSL::SSL::VERIFY_NONE ++ ++ conn.request_put("/production/#{endpoint}/#{certname}", yaml, headers) do |response| ++ response.read_body do |chunk| ++ puts chunk ++ end ++ end ] ++ ++ return exploit ++ end ++ ++ exploited = '/tmp/cve-2013-2274-has-worked' ++ restauth_conf = %q[ ++path /run ++auth yes ++allow * ++] ++ ++ teardown do ++ agents.each do |agent| ++ pidfile = on( agent, puppet_agent("--configprint pidfile") ).stdout.chomp ++ on agent, "[ -f #{pidfile} ] && kill `cat #{pidfile}` || true" ++ on agent, "rm -rf #{exploited}" ++ end ++ end ++ ++ agents.each do |agent| ++ atestdir = agent.tmpdir('puppet-kick-auth') ++ mtestdir = master.tmpdir('puppet-kick-auth') ++ ++ step "Daemonize the agent" do ++ # Lay down a tempory auth.conf that will allow the agent to be kicked ++ create_remote_file(agent, "#{atestdir}/auth.conf", restauth_conf) ++ ++ # Start the agent ++ on(agent, puppet_agent("--debug --daemonize --server #{master} --listen --no-client --rest_authconfig #{atestdir}/auth.conf")) ++ ++ step "Wait for agent to start listening" do ++ timeout = 15 ++ begin ++ Timeout.timeout(timeout) do ++ loop do ++ # 7 is "Could not connect to host", which will happen before it's running ++ result = on(agent, "curl -k https://#{agent}:8139", :acceptable_exit_codes => [0,7]) ++ break if result.exit_code == 0 ++ sleep 1 ++ end ++ end ++ rescue Timeout::Error ++ fail_test "Puppet agent #{agent} failed to start after #{timeout} seconds" ++ end ++ end ++ end ++ ++ step "Attempt to exploit #{agent}" do ++ # Ensure there's no stale data ++ on agent, "rm -rf #{exploited}" ++ on master, "rm -rf #{mtestdir}/exploit.rb" ++ ++ # Copy over our exploit and execute ++ create_remote_file( master, "#{mtestdir}/exploit.rb", exploit_code( master, agent, 'run', 8139, exploited )) ++ on master, "chmod +x #{mtestdir}/exploit.rb" ++ on master, "#{mtestdir}/exploit.rb" ++ ++ # Did it work? ++ fail_test( "Found exploit file #{exploited}" ) if ++ on( agent, "[ ! -f #{exploited} ]", ++ :acceptable_exit_codes => [0,1] ).exit_code == 1 ++ end ++ end ++end +Index: puppet-2.7.11/acceptance/tests/security/cve-2013-2274_all_your_master_terminii_belong_to_us.rb +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ puppet-2.7.11/acceptance/tests/security/cve-2013-2274_all_your_master_terminii_belong_to_us.rb 2013-03-11 12:32:08.855427540 -0400 +@@ -0,0 +1,79 @@ ++test_name "CVE 2013-2274" do ++ ++ step "Determine suitability of the test" do ++ confine :except, :platform => 'windows' ++ ++ versions = on( hosts, puppet( '--version' )) ++ skip_test( "This test will only run on Puppet 2.6" ) unless ++ versions.any? {|r| r.stdout =~ /\A2\.6\./ } ++ end ++ ++ ++ def exploit_code( exploiter, exploitee, endpoint, port, file_to_create, key=nil ) ++ ++ certfile = on( exploiter, puppet_agent( '--configprint hostcert' )).stdout.chomp ++ keyfile = on( exploiter, puppet_agent( '--configprint hostprivkey' )).stdout.chomp ++ certname = on( exploiter, puppet_agent( '--configprint certname' )).stdout.chomp ++ ++ exploit = %Q[#!/usr/bin/env ruby ++ require 'puppet' ++ require 'openssl' ++ require 'net/https' ++ ++ exec = Puppet::Type.type(:exec).new(:name => 'touch #{file_to_create}', :logoutput => true, :path => '/bin') ++ yaml = exec.to_resource.to_yaml ++ ++ headers = {'Content-Type' => 'text/yaml', 'Accept' => 'yaml'} ++ conn = Net::HTTP.new('#{exploitee}', #{port}) ++ conn.use_ssl = true ++ conn.cert = OpenSSL::X509::Certificate.new(File.read('#{certfile}')) ++ conn.key = OpenSSL::PKey::RSA.new(File.read('#{keyfile}')) ++ conn.verify_mode = OpenSSL::SSL::VERIFY_NONE ++ ++ conn.request_put("/production/#{endpoint}/#{key || certname}", yaml, headers) do |response| ++ response.read_body do |chunk| ++ puts chunk ++ end ++ end ] ++ ++ return exploit ++ end ++ ++ exploited = '/tmp/cve-2013-2274-has-worked' ++ ++ teardown do ++ agents.each do |agent| ++ pidfile = on( agent, puppet_agent("--configprint pidfile") ).stdout.chomp ++ on agent, "[ -f #{pidfile} ] && kill `cat #{pidfile}` || true" ++ on agent, "rm -rf #{exploited}" ++ end ++ end ++ ++ with_master_running_on(master, "--autosign true") do ++ agents.each do |agent| ++ ++ testdir = agent.tmpdir('puppet-kick-auth') ++ step "Prepare the agent:#{agent} to exploit the master #{master}" do ++ ++ # Ensure the agent has its cert ++ on agent, puppet_agent("--test --server #{master}") ++ ++ # Double check to ensure we don't have stale data ++ on master, "rm -rf #{exploited}" ++ on agent, "rm -rf #{testdir}/exploit.rb" ++ end ++ ++ step "Exploit the master" do ++ # Copy over our exploit code and execute it ++ create_remote_file( agent, "#{testdir}/exploit.rb", exploit_code( agent, master, 'report', 8140, exploited )) ++ on agent, "chmod +x #{testdir}/exploit.rb" ++ on agent, "#{testdir}/exploit.rb" ++ ++ # Did it exploit the SUT? ++ fail_test( "Found exploit file #{exploited}" ) if ++ on( master, "[ ! -f #{exploited} ]", ++ :acceptable_exit_codes => [0,1] ).exit_code == 1 ++ end ++ end ++ end ++end +Index: puppet-2.7.11/acceptance/tests/security/cve-2013-2275_report_acl.rb +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ puppet-2.7.11/acceptance/tests/security/cve-2013-2275_report_acl.rb 2013-03-11 12:32:08.855427540 -0400 +@@ -0,0 +1,30 @@ ++test_name "(#19531) report save access control" ++step "Verify puppet only allows saving reports from the node matching the certificate" ++ ++fake_report = <<-EOYAML ++--- !ruby/object:Puppet::Transaction::Report ++ host: mccune ++ metrics: {} ++ logs: [] ++ kind: inspect ++ puppet_version: "2.7.20" ++ status: failed ++ report_format: 3 ++EOYAML ++ ++with_master_running_on(master) do ++ submit_fake_report_cmd = [ ++ "curl -k -X PUT", ++ "--cacert \"$(puppet master --configprint cacert)\"", ++ "--cert \"$(puppet master --configprint hostcert)\"", ++ "--key \"$(puppet master --configprint hostprivkey)\"", ++ "-H 'Content-Type: text/yaml'", ++ "-d '#{fake_report}'", ++ "\"https://#{master}:8140/production/report/mccune\"", ++ ].join(" ") ++ ++ on master, submit_fake_report_cmd, :acceptable_exit_codes => [0] do ++ msg = "(#19531) (CVE-2013-2275) Puppet master accepted a report for a node that does not match the certname" ++ assert_match(/Forbidden request/, stdout, msg) ++ end ++end +Index: puppet-2.7.11/conf/auth.conf +=================================================================== +--- puppet-2.7.11.orig/conf/auth.conf 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/conf/auth.conf 2013-03-11 12:32:08.855427540 -0400 +@@ -63,10 +63,10 @@ + method find + allow * + +-# allow all nodes to store their reports +-path /report ++# allow all nodes to store their own reports ++path ~ ^/report/([^/]+)$ + method save +-allow * ++allow $1 + + # inconditionnally allow access to all files services + # which means in practice that fileserver.conf will +Index: puppet-2.7.11/lib/puppet/indirector/catalog/compiler.rb +=================================================================== +--- puppet-2.7.11.orig/lib/puppet/indirector/catalog/compiler.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/lib/puppet/indirector/catalog/compiler.rb 2013-03-11 12:32:08.855427540 -0400 +@@ -13,7 +13,9 @@ + + def extract_facts_from_request(request) + return unless text_facts = request.options[:facts] +- raise ArgumentError, "Facts but no fact format provided for #{request.name}" unless format = request.options[:facts_format] ++ unless format = request.options[:facts_format] ++ raise ArgumentError, "Facts but no fact format provided for #{request.key}" ++ end + + # If the facts were encoded as yaml, then the param reconstitution system + # in Network::HTTP::Handler will automagically deserialize the value. +@@ -22,6 +24,11 @@ + else + facts = Puppet::Node::Facts.convert_from(format, text_facts) + end ++ ++ unless facts.name == request.key ++ raise Puppet::Error, "Catalog for #{request.key.inspect} was requested with fact definition for the wrong node (#{facts.name.inspect})." ++ end ++ + facts.add_timestamp + Puppet::Node::Facts.indirection.save(facts) + end +@@ -104,7 +111,11 @@ + # to find the node. + def node_from_request(request) + if node = request.options[:use_node] +- return node ++ if request.remote? ++ raise Puppet::Error, "Invalid option use_node for a remote request" ++ else ++ return node ++ end + end + + # We rely on our authorization system to determine whether the connected +Index: puppet-2.7.11/lib/puppet/indirector/certificate_status/file.rb +=================================================================== +--- puppet-2.7.11.orig/lib/puppet/indirector/certificate_status/file.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/lib/puppet/indirector/certificate_status/file.rb 2013-03-11 12:32:08.855427540 -0400 +@@ -79,4 +79,9 @@ + nil + end + end ++ ++ def validate_key(request) ++ # We only use desired_state from the instance and use request.key ++ # otherwise, so the name does not need to match ++ end + end +Index: puppet-2.7.11/lib/puppet/indirector/errors.rb +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ puppet-2.7.11/lib/puppet/indirector/errors.rb 2013-03-11 12:32:08.855427540 -0400 +@@ -0,0 +1,5 @@ ++require 'puppet/error' ++ ++module Puppet::Indirector ++ class ValidationError < Puppet::Error; end ++end +Index: puppet-2.7.11/lib/puppet/indirector/file_bucket_file/file.rb +=================================================================== +--- puppet-2.7.11.orig/lib/puppet/indirector/file_bucket_file/file.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/lib/puppet/indirector/file_bucket_file/file.rb 2013-03-11 12:32:08.855427540 -0400 +@@ -48,6 +48,10 @@ + instance.to_s + end + ++ def validate_key(request) ++ # There are no ACLs on filebucket files so validating key is not important ++ end ++ + private + + def path_match(dir_path, files_original_path) +Index: puppet-2.7.11/lib/puppet/indirector/indirection.rb +=================================================================== +--- puppet-2.7.11.orig/lib/puppet/indirector/indirection.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/lib/puppet/indirector/indirection.rb 2013-03-11 12:32:08.855427540 -0400 +@@ -309,6 +309,7 @@ + + dest_terminus = terminus(terminus_name) + check_authorization(request, dest_terminus) ++ dest_terminus.validate(request) + + dest_terminus + end +Index: puppet-2.7.11/lib/puppet/indirector/request.rb +=================================================================== +--- puppet-2.7.11.orig/lib/puppet/indirector/request.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/lib/puppet/indirector/request.rb 2013-03-11 12:32:08.855427540 -0400 +@@ -150,6 +150,10 @@ + return(uri ? uri : "/#{indirection_name}/#{key}") + end + ++ def remote? ++ self.node or self.ip ++ end ++ + private + + def set_attributes(options) +Index: puppet-2.7.11/lib/puppet/indirector/resource/active_record.rb +=================================================================== +--- puppet-2.7.11.orig/lib/puppet/indirector/resource/active_record.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/lib/puppet/indirector/resource/active_record.rb 2013-03-11 12:32:08.855427540 -0400 +@@ -1,6 +1,9 @@ + require 'puppet/indirector/active_record' ++require 'puppet/indirector/resource/validator' + + class Puppet::Resource::ActiveRecord < Puppet::Indirector::ActiveRecord ++ include Puppet::Resource::Validator ++ + def search(request) + type = request_to_type_name(request) + host = request.options[:host] +Index: puppet-2.7.11/lib/puppet/indirector/resource/ral.rb +=================================================================== +--- puppet-2.7.11.orig/lib/puppet/indirector/resource/ral.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/lib/puppet/indirector/resource/ral.rb 2013-03-11 12:32:08.859427541 -0400 +@@ -1,4 +1,8 @@ ++require 'puppet/indirector/resource/validator' ++ + class Puppet::Resource::Ral < Puppet::Indirector::Code ++ include Puppet::Resource::Validator ++ + def find( request ) + # find by name + res = type(request).instances.find { |o| o.name == resource_name(request) } +Index: puppet-2.7.11/lib/puppet/indirector/resource/store_configs.rb +=================================================================== +--- puppet-2.7.11.orig/lib/puppet/indirector/resource/store_configs.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/lib/puppet/indirector/resource/store_configs.rb 2013-03-11 12:32:08.859427541 -0400 +@@ -1,3 +1,6 @@ + require 'puppet/indirector/store_configs' ++require 'puppet/indirector/resource/validator' ++ + class Puppet::Resource::StoreConfigs < Puppet::Indirector::StoreConfigs ++ include Puppet::Resource::Validator + end +Index: puppet-2.7.11/lib/puppet/indirector/resource/validator.rb +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ puppet-2.7.11/lib/puppet/indirector/resource/validator.rb 2013-03-11 12:32:08.859427541 -0400 +@@ -0,0 +1,8 @@ ++module Puppet::Resource::Validator ++ def validate_key(request) ++ type, title = request.key.split('/', 2) ++ unless type.downcase == request.instance.type.downcase and title == request.instance.title ++ raise Puppet::Indirector::ValidationError, "Resource instance does not match request key" ++ end ++ end ++end +Index: puppet-2.7.11/lib/puppet/indirector/rest.rb +=================================================================== +--- puppet-2.7.11.orig/lib/puppet/indirector/rest.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/lib/puppet/indirector/rest.rb 2013-03-11 12:32:08.859427541 -0400 +@@ -102,6 +102,10 @@ + msg = valid_certnames.length > 1 ? "one of #{valid_certnames.join(', ')}" : valid_certnames.first + + raise Puppet::Error, "Server hostname '#{http_connection.address}' did not match server certificate; expected #{msg}" ++ elsif error.message.empty? ++ # This may be because the server is speaking SSLv2 and we ++ # monkey patch OpenSSL::SSL:SSLContext to reject SSLv2. ++ raise error.exception("#{error.class} with no message") + else + raise + end +@@ -152,6 +156,10 @@ + deserialize http_put(request, indirection2uri(request), request.instance.render, headers.merge({ "Content-Type" => request.instance.mime })) + end + ++ def validate_key(request) ++ # Validation happens on the remote end ++ end ++ + private + + def environment +Index: puppet-2.7.11/lib/puppet/indirector/run/local.rb +=================================================================== +--- puppet-2.7.11.orig/lib/puppet/indirector/run/local.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/lib/puppet/indirector/run/local.rb 2013-03-11 12:32:08.859427541 -0400 +@@ -5,4 +5,8 @@ + def save( request ) + request.instance.run + end ++ ++ def validate_key(request) ++ # No key is necessary for kick ++ end + end +Index: puppet-2.7.11/lib/puppet/indirector/terminus.rb +=================================================================== +--- puppet-2.7.11.orig/lib/puppet/indirector/terminus.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/lib/puppet/indirector/terminus.rb 2013-03-11 12:32:08.859427541 -0400 +@@ -1,4 +1,5 @@ + require 'puppet/indirector' ++require 'puppet/indirector/errors' + require 'puppet/indirector/indirection' + require 'puppet/util/instance_loader' + +@@ -142,4 +143,23 @@ + def terminus_type + self.class.terminus_type + end ++ ++ def validate(request) ++ if request.instance ++ validate_key(request) ++ validate_model(request) ++ end ++ end ++ ++ def validate_key(request) ++ unless request.key == request.instance.name ++ raise Puppet::Indirector::ValidationError, "Instance name #{request.instance.name.inspect} does not match requested key #{request.key.inspect}" ++ end ++ end ++ ++ def validate_model(request) ++ unless request.instance.kind_of?(model) ++ raise Puppet::Indirector::ValidationError, "Invalid instance type #{request.instance.class.inspect}, expected #{model.inspect}" ++ end ++ end + end +Index: puppet-2.7.11/lib/puppet/network/formats.rb +=================================================================== +--- puppet-2.7.11.orig/lib/puppet/network/formats.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/lib/puppet/network/formats.rb 2013-03-11 12:32:08.859427541 -0400 +@@ -3,12 +3,12 @@ + Puppet::Network::FormatHandler.create_serialized_formats(:yaml) do + # Yaml doesn't need the class name; it's serialized. + def intern(klass, text) +- YAML.load(text) ++ YAML.safely_load(text) + end + + # Yaml doesn't need the class name; it's serialized. + def intern_multiple(klass, text) +- YAML.load(text) ++ YAML.safely_load(text) + end + + def render(instance) +@@ -72,7 +72,7 @@ + + def decode(yaml) + requiring_zlib do +- YAML.load(Zlib::Inflate.inflate(Base64.decode64(yaml))) ++ YAML.safely_load(Zlib::Inflate.inflate(Base64.decode64(yaml))) + end + end + end +Index: puppet-2.7.11/lib/puppet/network/handler/master.rb +=================================================================== +--- puppet-2.7.11.orig/lib/puppet/network/handler/master.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/lib/puppet/network/handler/master.rb 2013-03-11 12:32:08.859427541 -0400 +@@ -69,7 +69,7 @@ + Puppet.debug "Our client is remote" + + begin +- facts = YAML.load(CGI.unescape(facts)) ++ facts = YAML.safely_load(CGI.unescape(facts)) + rescue => detail + raise XMLRPC::FaultException.new( + 1, "Could not rebuild facts" +Index: puppet-2.7.11/lib/puppet/network/handler/report.rb +=================================================================== +--- puppet-2.7.11.orig/lib/puppet/network/handler/report.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/lib/puppet/network/handler/report.rb 2013-03-11 12:32:08.859427541 -0400 +@@ -45,7 +45,7 @@ + + # First convert the report to real objects + begin +- report = YAML.load(yaml) ++ report = YAML.safely_load(yaml) + rescue => detail + Puppet.warning "Could not load report: #{detail}" + return +Index: puppet-2.7.11/lib/puppet/network/http/handler.rb +=================================================================== +--- puppet-2.7.11.orig/lib/puppet/network/http/handler.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/lib/puppet/network/http/handler.rb 2013-03-11 12:32:08.859427541 -0400 +@@ -70,6 +70,8 @@ + raise + rescue Exception => e + return do_exception(response, e) ++ ensure ++ cleanup(request) + end + + # Set the response up, with the body and status. +@@ -217,6 +219,10 @@ + raise NotImplementedError + end + ++ def cleanup(request) ++ # By default, there is nothing to cleanup. ++ end ++ + def decode_params(params) + params.inject({}) do |result, ary| + param, value = ary +@@ -230,7 +236,7 @@ + next result if param == :ip + value = CGI.unescape(value) + if value =~ /^---/ +- value = YAML.load(value) ++ value = YAML.safely_load(value) + else + value = true if value == "true" + value = false if value == "false" +Index: puppet-2.7.11/lib/puppet/network/http/rack/rest.rb +=================================================================== +--- puppet-2.7.11.orig/lib/puppet/network/http/rack/rest.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/lib/puppet/network/http/rack/rest.rb 2013-03-11 12:32:08.859427541 -0400 +@@ -73,12 +73,17 @@ + end + + # return the request body +- # request.body has some limitiations, so we need to concat it back +- # into a regular string, which is something puppet can use. + def body(request) + request.body.read + end + ++ # Passenger freaks out if we finish handling the request without reading any ++ # part of the body, so make sure we have. ++ def cleanup(request) ++ request.body.read(1) ++ nil ++ end ++ + def extract_client_info(request) + result = {} + result[:ip] = request.ip +Index: puppet-2.7.11/lib/puppet/network/http/webrick.rb +=================================================================== +--- puppet-2.7.11.orig/lib/puppet/network/http/webrick.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/lib/puppet/network/http/webrick.rb 2013-03-11 12:32:08.859427541 -0400 +@@ -104,6 +104,7 @@ + results[:SSLCertificate] = host.certificate.content + results[:SSLStartImmediately] = true + results[:SSLEnable] = true ++ results[:SSLOptions] = OpenSSL::SSL::OP_NO_SSLv2 + + raise Puppet::Error, "Could not find CA certificate" unless Puppet::SSL::Certificate.indirection.find(Puppet::SSL::CA_NAME) + +Index: puppet-2.7.11/lib/puppet/network/http_pool.rb +=================================================================== +--- puppet-2.7.11.orig/lib/puppet/network/http_pool.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/lib/puppet/network/http_pool.rb 2013-03-11 12:32:08.859427541 -0400 +@@ -11,14 +11,23 @@ + + # Use cert information from a Puppet client to set up the http object. + def self.cert_setup(http) +- # Just no-op if we don't have certs. +- return false unless FileTest.exist?(Puppet[:hostcert]) and FileTest.exist?(Puppet[:localcacert]) ++ if FileTest.exist?(Puppet[:hostcert]) and FileTest.exist?(Puppet[:localcacert]) ++ http.cert_store = ssl_host.ssl_store ++ http.ca_file = Puppet[:localcacert] ++ http.cert = ssl_host.certificate.content ++ http.verify_mode = OpenSSL::SSL::VERIFY_PEER ++ http.key = ssl_host.key.content ++ else ++ # We don't have the local certificates, so we don't do any verification ++ # or setup at this early stage. REVISIT: Shouldn't we supply the local ++ # certificate details if we have them? The original code didn't. ++ # --daniel 2012-06-03 + +- http.cert_store = ssl_host.ssl_store +- http.ca_file = Puppet[:localcacert] +- http.cert = ssl_host.certificate.content +- http.verify_mode = OpenSSL::SSL::VERIFY_PEER +- http.key = ssl_host.key.content ++ # Ruby 1.8 defaulted to this, but 1.9 defaults to peer verify, and we ++ # almost always talk to a dedicated, not-standard CA that isn't trusted ++ # out of the box. This forces the expected state. ++ http.verify_mode = OpenSSL::SSL::VERIFY_NONE ++ end + end + + # Retrieve a cached http instance if caching is enabled, else return +Index: puppet-2.7.11/lib/puppet/network/rest_authconfig.rb +=================================================================== +--- puppet-2.7.11.orig/lib/puppet/network/rest_authconfig.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/lib/puppet/network/rest_authconfig.rb 2013-03-11 12:32:08.863427541 -0400 +@@ -13,7 +13,7 @@ + # to fileserver.conf + { :acl => "/file" }, + { :acl => "/certificate_revocation_list/ca", :method => :find, :authenticated => true }, +- { :acl => "/report", :method => :save, :authenticated => true }, ++ { :acl => "~ ^\/report\/([^\/]+)$", :method => :save, :allow => '$1', :authenticated => true }, + # These allow `auth any`, because if you can do them anonymously you + # should probably also be able to do them when trusted. + { :acl => "/certificate/ca", :method => :find, :authenticated => :any }, +Index: puppet-2.7.11/lib/puppet/parser/templatewrapper.rb +=================================================================== +--- puppet-2.7.11.orig/lib/puppet/parser/templatewrapper.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/lib/puppet/parser/templatewrapper.rb 2013-03-11 12:32:08.863427541 -0400 +@@ -5,8 +5,6 @@ + + class Puppet::Parser::TemplateWrapper + attr_writer :scope +- attr_reader :file +- attr_accessor :string + include Puppet::Util + Puppet::Util.logmethods(self) + +@@ -14,18 +12,22 @@ + @__scope__ = scope + end + ++ def file ++ @__file__ ++ end ++ + def scope + @__scope__ + end + + def script_line + # find which line in the template (if any) we were called from +- (caller.find { |l| l =~ /#{file}:/ }||"")[/:(\d+):/,1] ++ (caller.find { |l| l =~ /#{@__file__}:/ }||"")[/:(\d+):/,1] + end + + # Should return true if a variable is defined, false if it is not + def has_variable?(name) +- scope.lookupvar(name.to_s, :file => file, :line => script_line) != :undefined ++ scope.lookupvar(name.to_s, :file => @__file__, :line => script_line) != :undefined + end + + # Allow templates to access the defined classes +@@ -56,53 +58,51 @@ + # the missing_method definition here until we declare the syntax finally + # dead. + def method_missing(name, *args) +- value = scope.lookupvar(name.to_s,:file => file,:line => script_line) ++ value = scope.lookupvar(name.to_s,:file => @__file__,:line => script_line) + if value != :undefined + return value + else + # Just throw an error immediately, instead of searching for + # other missingmethod things or whatever. +- raise Puppet::ParseError.new("Could not find value for '#{name}'",@file,script_line) ++ raise Puppet::ParseError.new("Could not find value for '#{name}'", @__file__, script_line) + end + end + + def file=(filename) +- unless @file = Puppet::Parser::Files.find_template(filename, scope.compiler.environment.to_s) ++ unless @__file__ = Puppet::Parser::Files.find_template(filename, scope.compiler.environment.to_s) + raise Puppet::ParseError, "Could not find template '#{filename}'" + end + + # We'll only ever not have a parser in testing, but, eh. +- scope.known_resource_types.watch_file(file) +- +- @string = File.read(file) ++ scope.known_resource_types.watch_file(@__file__) + end + + def result(string = nil) + if string +- self.string = string + template_source = "inline template" + else +- template_source = file ++ string = File.read(@__file__) ++ template_source = @__file__ + end + + # Expose all the variables in our scope as instance variables of the + # current object, making it possible to access them without conflict + # to the regular methods. + benchmark(:debug, "Bound template variables for #{template_source}") do +- scope.to_hash.each { |name, value| ++ scope.to_hash.each do |name, value| + if name.kind_of?(String) + realname = name.gsub(/[^\w]/, "_") + else + realname = name + end + instance_variable_set("@#{realname}", value) +- } ++ end + end + + result = nil + benchmark(:debug, "Interpolated template #{template_source}") do +- template = ERB.new(self.string, 0, "-") +- template.filename = file ++ template = ERB.new(string, 0, "-") ++ template.filename = @__file__ + result = template.result(binding) + end + +@@ -110,6 +110,6 @@ + end + + def to_s +- "template[#{(file ? file : "inline")}]" ++ "template[#{(@__file__ ? @__file__ : "inline")}]" + end + end +Index: puppet-2.7.11/lib/puppet/util/monkey_patches.rb +=================================================================== +--- puppet-2.7.11.orig/lib/puppet/util/monkey_patches.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/lib/puppet/util/monkey_patches.rb 2013-03-11 12:32:08.863427541 -0400 +@@ -34,6 +34,21 @@ + end + } + ++if defined?(YAML::ENGINE) and YAML::ENGINE.yamler == 'psych' ++ def Psych.safely_load(str) ++ result = Psych.parse(str) ++ if invalid_node = result.find { |node| node.tag =~ /!map:(.*)/ || node.tag =~ /!ruby\/hash:(.*)/ } ++ raise ArgumentError, "Illegal YAML mapping found with tag #{invalid_node.tag}; please use !ruby/object:#{$1} instead" ++ else ++ result.to_ruby ++ end ++ end ++else ++ def YAML.safely_load(str) ++ self.load(str) ++ end ++end ++ + def YAML.dump(*args) + ZAML.dump(*args) + end +@@ -138,3 +153,91 @@ + self + end unless method_defined?(:tap) + end ++ ++# The mv method in Ruby 1.8.5 can't mv directories across devices ++# File.rename causes "Invalid cross-device link", which is rescued, but in Ruby ++# 1.8.5 it tries to recover with a copy and unlink, but the unlink causes the ++# error "Is a directory". In newer Rubies remove_entry is used ++# The implementation below is what's used in Ruby 1.8.7 and Ruby 1.9 ++if RUBY_VERSION == '1.8.5' ++ require 'fileutils' ++ ++ module FileUtils ++ def mv(src, dest, options = {}) ++ fu_check_options options, OPT_TABLE['mv'] ++ fu_output_message "mv#{options[:force] ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose] ++ return if options[:noop] ++ fu_each_src_dest(src, dest) do |s, d| ++ destent = Entry_.new(d, nil, true) ++ begin ++ if destent.exist? ++ if destent.directory? ++ raise Errno::EEXIST, dest ++ else ++ destent.remove_file if rename_cannot_overwrite_file? ++ end ++ end ++ begin ++ File.rename s, d ++ rescue Errno::EXDEV ++ copy_entry s, d, true ++ if options[:secure] ++ remove_entry_secure s, options[:force] ++ else ++ remove_entry s, options[:force] ++ end ++ end ++ rescue SystemCallError ++ raise unless options[:force] ++ end ++ end ++ end ++ module_function :mv ++ ++ alias move mv ++ module_function :move ++ end ++end ++ ++# (#19151) Reject all SSLv2 ciphers and handshakes ++require 'openssl' ++class OpenSSL::SSL::SSLContext ++ if match = /^1\.8\.(\d+)/.match(RUBY_VERSION) ++ older_than_187 = match[1].to_i < 7 ++ else ++ older_than_187 = false ++ end ++ ++ alias __original_initialize initialize ++ private :__original_initialize ++ ++ if older_than_187 ++ def initialize(*args) ++ __original_initialize(*args) ++ if bitmask = self.options ++ self.options = bitmask | OpenSSL::SSL::OP_NO_SSLv2 ++ else ++ self.options = OpenSSL::SSL::OP_NO_SSLv2 ++ end ++ # These are the default ciphers in recent MRI versions. See ++ # https://github.com/ruby/ruby/blob/v1_9_3_392/ext/openssl/lib/openssl/ssl-internal.rb#L26 ++ self.ciphers = "ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW" ++ end ++ else ++ if DEFAULT_PARAMS[:options] ++ DEFAULT_PARAMS[:options] |= OpenSSL::SSL::OP_NO_SSLv2 ++ else ++ DEFAULT_PARAMS[:options] = OpenSSL::SSL::OP_NO_SSLv2 ++ end ++ DEFAULT_PARAMS[:ciphers] << ':!SSLv2' ++ ++ def initialize(*args) ++ __original_initialize(*args) ++ params = { ++ :options => DEFAULT_PARAMS[:options], ++ :ciphers => DEFAULT_PARAMS[:ciphers], ++ } ++ set_params(params) ++ end ++ end ++end +Index: puppet-2.7.11/spec/integration/indirector/catalog/compiler_spec.rb +=================================================================== +--- puppet-2.7.11.orig/spec/integration/indirector/catalog/compiler_spec.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/spec/integration/indirector/catalog/compiler_spec.rb 2013-03-11 12:32:08.863427541 -0400 +@@ -11,6 +11,7 @@ + @catalog = Puppet::Resource::Catalog.new + @catalog.add_resource(@one = Puppet::Resource.new(:file, "/one")) + @catalog.add_resource(@two = Puppet::Resource.new(:file, "/two")) ++ Puppet::Resource::Catalog.indirection.terminus.stubs(:validate) + end + + after { Puppet.settings.clear } +Index: puppet-2.7.11/spec/integration/indirector/catalog/queue_spec.rb +=================================================================== +--- puppet-2.7.11.orig/spec/integration/indirector/catalog/queue_spec.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/spec/integration/indirector/catalog/queue_spec.rb 2013-03-11 12:32:08.863427541 -0400 +@@ -6,7 +6,7 @@ + describe "Puppet::Resource::Catalog::Queue", :if => Puppet.features.pson? do + before do + Puppet::Resource::Catalog.indirection.terminus(:queue) +- @catalog = Puppet::Resource::Catalog.new ++ @catalog = Puppet::Resource::Catalog.new("foo") + + @one = Puppet::Resource.new(:file, "/one") + @two = Puppet::Resource.new(:file, "/two") +Index: puppet-2.7.11/spec/integration/resource/catalog_spec.rb +=================================================================== +--- puppet-2.7.11.orig/spec/integration/resource/catalog_spec.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/spec/integration/resource/catalog_spec.rb 2013-03-11 12:32:08.863427541 -0400 +@@ -48,6 +48,7 @@ + Puppet::Resource::Catalog.indirection.stubs(:terminus).returns terminus + + node = mock 'node' ++ terminus.stubs(:validate) + terminus.expects(:find).with { |request| request.options[:use_node] == node } + Puppet::Resource::Catalog.indirection.find("me", :use_node => node) + end +Index: puppet-2.7.11/spec/unit/indirector/catalog/compiler_spec.rb +=================================================================== +--- puppet-2.7.11.orig/spec/unit/indirector/catalog/compiler_spec.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/spec/unit/indirector/catalog/compiler_spec.rb 2013-03-11 12:32:08.863427541 -0400 +@@ -53,13 +53,22 @@ + @request = stub 'request', :key => @name, :node => @name, :options => {} + end + +- it "should directly use provided nodes" do ++ it "should directly use provided nodes for a local request" do + Puppet::Node.indirection.expects(:find).never + @compiler.expects(:compile).with(@node) + @request.stubs(:options).returns(:use_node => @node) ++ @request.stubs(:remote?).returns(false) + @compiler.find(@request) + end + ++ it "rejects a provided node if the request is remote" do ++ @request.stubs(:options).returns(:use_node => @node) ++ @request.stubs(:remote?).returns(true) ++ expect { ++ @compiler.find(@request) ++ }.to raise_error Puppet::Error, /invalid option use_node/i ++ end ++ + it "should use the authenticated node name if no request key is provided" do + @request.stubs(:key).returns(nil) + Puppet::Node.indirection.expects(:find).with(@name).returns(@node) +@@ -99,6 +108,24 @@ + @compiler.find(@request) + end + ++ it "requires `facts_format` option if facts are passed in" do ++ facts = Puppet::Node::Facts.new("mynode", :afact => "avalue") ++ request = Puppet::Indirector::Request.new(:catalog, :find, "mynode", :facts => facts) ++ expect { ++ @compiler.find(request) ++ }.to raise_error ArgumentError, /no fact format provided for mynode/ ++ end ++ ++ it "rejects facts in the request from a different node" do ++ facts = Puppet::Node::Facts.new("differentnode", :afact => "avalue") ++ request = Puppet::Indirector::Request.new( ++ :catalog, :find, "mynode", :facts => facts, :facts_format => "unused" ++ ) ++ expect { ++ @compiler.find(request) ++ }.to raise_error Puppet::Error, /fact definition for the wrong node/i ++ end ++ + it "should return the results of compiling as the catalog" do + Puppet::Node.indirection.stubs(:find).returns(@node) + config = mock 'config' +@@ -133,7 +160,7 @@ + before do + Facter.stubs(:value).returns "something" + @compiler = Puppet::Resource::Catalog::Compiler.new +- @request = stub 'request', :options => {} ++ @request = Puppet::Indirector::Request.new(:catalog, :find, "hostname", nil) + + @facts = Puppet::Node::Facts.new('hostname', "fact" => "value", "architecture" => "i386") + Puppet::Node::Facts.indirection.stubs(:save).returns(nil) +Index: puppet-2.7.11/spec/unit/indirector/indirection_spec.rb +=================================================================== +--- puppet-2.7.11.orig/spec/unit/indirector/indirection_spec.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/spec/unit/indirector/indirection_spec.rb 2013-03-11 12:32:08.863427541 -0400 +@@ -40,7 +40,7 @@ + end + end + +- request = stub 'request', :key => "me", :options => {} ++ request = Puppet::Indirector::Request.new(:indirection, :find, "me", nil) + + @indirection.stubs(:request).returns request + +@@ -101,6 +101,16 @@ + end + end + ++shared_examples_for "Request validator" do ++ it "asks the terminus to validate the request" do ++ @terminus.expects(:validate).raises(Puppet::Indirector::ValidationError, "Invalid") ++ @terminus.expects(@method).never ++ expect { ++ @indirection.send(@method, "key") ++ }.to raise_error Puppet::Indirector::ValidationError ++ end ++end ++ + describe Puppet::Indirector::Indirection do + describe "when initializing" do + # (LAK) I've no idea how to test this, really. +@@ -141,6 +151,7 @@ + before :each do + @terminus_class = mock 'terminus_class' + @terminus = mock 'terminus' ++ @terminus.stubs(:validate) + @terminus_class.stubs(:new).returns(@terminus) + @cache = stub 'cache', :name => "mycache" + @cache_class = mock 'cache_class' +@@ -211,6 +222,7 @@ + + it_should_behave_like "Indirection Delegator" + it_should_behave_like "Delegation Authorizer" ++ it_should_behave_like "Request validator" + + it "should return the results of the delegation" do + @terminus.expects(:find).returns(@instance) +@@ -251,6 +263,7 @@ + before do + @indirection.cache_class = :cache_terminus + @cache_class.stubs(:new).returns(@cache) ++ @cache.stubs(:validate) + + @instance.stubs(:expired?).returns false + end +@@ -384,6 +397,7 @@ + + it_should_behave_like "Indirection Delegator" + it_should_behave_like "Delegation Authorizer" ++ it_should_behave_like "Request validator" + + it "should return true if the head method returned true" do + @terminus.expects(:head).returns(true) +@@ -501,6 +515,7 @@ + + it_should_behave_like "Indirection Delegator" + it_should_behave_like "Delegation Authorizer" ++ it_should_behave_like "Request validator" + + it "should return the result of removing the instance" do + @terminus.stubs(:destroy).returns "yayness" +@@ -539,6 +554,7 @@ + + it_should_behave_like "Indirection Delegator" + it_should_behave_like "Delegation Authorizer" ++ it_should_behave_like "Request validator" + + it "should set the expiration date on any instances without one set" do + @terminus.stubs(:search).returns([@instance]) +@@ -707,6 +723,7 @@ + before do + @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) + @terminus = mock 'terminus' ++ @terminus.stubs(:validate) + @terminus_class = stub 'terminus class', :new => @terminus + end + +Index: puppet-2.7.11/spec/unit/indirector/request_spec.rb +=================================================================== +--- puppet-2.7.11.orig/spec/unit/indirector/request_spec.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/spec/unit/indirector/request_spec.rb 2013-03-11 12:32:08.863427541 -0400 +@@ -311,4 +311,26 @@ + lambda { @request.query_string }.should raise_error(ArgumentError) + end + end ++ ++ describe "#remote?" do ++ def request(options = {}) ++ Puppet::Indirector::Request.new('node', 'find', 'localhost', options) ++ end ++ ++ it "should not be unless node or ip is set" do ++ request.should_not be_remote ++ end ++ ++ it "should be remote if node is set" do ++ request(:node => 'example.com').should be_remote ++ end ++ ++ it "should be remote if ip is set" do ++ request(:ip => '127.0.0.1').should be_remote ++ end ++ ++ it "should be remote if node and ip are set" do ++ request(:node => 'example.com', :ip => '127.0.0.1').should be_remote ++ end ++ end + end +Index: puppet-2.7.11/spec/unit/indirector/terminus_spec.rb +=================================================================== +--- puppet-2.7.11.orig/spec/unit/indirector/terminus_spec.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/spec/unit/indirector/terminus_spec.rb 2013-03-11 12:32:08.867427541 -0400 +@@ -1,250 +1,264 @@ +-#!/usr/bin/env rspec ++#! /usr/bin/env ruby + require 'spec_helper' + require 'puppet/defaults' + require 'puppet/indirector' + require 'puppet/indirector/memory' + +-describe Puppet::Indirector::Terminus, :'fails_on_ruby_1.9.2' => true do +- before :each do +- Puppet::Indirector::Terminus.stubs(:register_terminus_class) +- @indirection = stub 'indirection', :name => :my_stuff, :register_terminus_type => nil +- Puppet::Indirector::Indirection.stubs(:instance).with(:my_stuff).returns(@indirection) +- @abstract_terminus = Class.new(Puppet::Indirector::Terminus) do +- def self.to_s +- "Testing::Abstract" ++describe Puppet::Indirector::Terminus do ++ before :all do ++ class Puppet::AbstractConcept ++ extend Puppet::Indirector ++ indirects :abstract_concept ++ attr_accessor :name ++ def initialize(name = "name") ++ @name = name + end + end +- @terminus_class = Class.new(@abstract_terminus) do +- def self.to_s +- "MyStuff::TermType" +- end ++ ++ class Puppet::AbstractConcept::Freedom < Puppet::Indirector::Code + end +- @terminus = @terminus_class.new + end + +- describe Puppet::Indirector::Terminus do ++ after :all do ++ # Remove the class, unlinking it from the rest of the system. ++ Puppet.send(:remove_const, :AbstractConcept) ++ end + +- it "should provide a method for setting terminus class documentation" do +- @terminus_class.should respond_to(:desc) +- end ++ let :terminus_class do Puppet::AbstractConcept::Freedom end ++ let :terminus do terminus_class.new end ++ let :indirection do Puppet::AbstractConcept.indirection end ++ let :model do Puppet::AbstractConcept end + +- it "should support a class-level name attribute" do +- @terminus_class.should respond_to(:name) +- end ++ it "should provide a method for setting terminus class documentation" do ++ terminus_class.should respond_to(:desc) ++ end + +- it "should support a class-level indirection attribute" do +- @terminus_class.should respond_to(:indirection) +- end ++ it "should support a class-level name attribute" do ++ terminus_class.should respond_to(:name) ++ end + +- it "should support a class-level terminus-type attribute" do +- @terminus_class.should respond_to(:terminus_type) +- end ++ it "should support a class-level indirection attribute" do ++ terminus_class.should respond_to(:indirection) ++ end + +- it "should support a class-level model attribute" do +- @terminus_class.should respond_to(:model) +- end ++ it "should support a class-level terminus-type attribute" do ++ terminus_class.should respond_to(:terminus_type) ++ end + +- it "should accept indirection instances as its indirection" do +- indirection = stub 'indirection', :is_a? => true, :register_terminus_type => nil +- proc { @terminus_class.indirection = indirection }.should_not raise_error +- @terminus_class.indirection.should equal(indirection) +- end ++ it "should support a class-level model attribute" do ++ terminus_class.should respond_to(:model) ++ end + +- it "should look up indirection instances when only a name has been provided" do +- indirection = mock 'indirection' +- Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns(indirection) +- @terminus_class.indirection = :myind +- @terminus_class.indirection.should equal(indirection) +- end ++ it "should accept indirection instances as its indirection" do ++ # The test is that this shouldn't raise, and should preserve the object ++ # instance exactly, hence "equal", not just "==". ++ terminus_class.indirection = indirection ++ terminus_class.indirection.should equal indirection ++ end + +- it "should fail when provided a name that does not resolve to an indirection" do +- Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns(nil) +- proc { @terminus_class.indirection = :myind }.should raise_error(ArgumentError) ++ it "should look up indirection instances when only a name has been provided" do ++ terminus_class.indirection = :abstract_concept ++ terminus_class.indirection.should equal indirection ++ end + +- # It shouldn't overwrite our existing one (or, more normally, it shouldn't set +- # anything). +- @terminus_class.indirection.should equal(@indirection) +- end ++ it "should fail when provided a name that does not resolve to an indirection" do ++ expect { ++ terminus_class.indirection = :exploding_whales ++ }.to raise_error(ArgumentError, /Could not find indirection instance/) ++ ++ # We should still have the default indirection. ++ terminus_class.indirection.should equal indirection + end + +- describe Puppet::Indirector::Terminus, " when creating terminus classes" do +- it "should associate the subclass with an indirection based on the subclass constant" do +- @terminus.indirection.should equal(@indirection) ++ describe "when a terminus instance" do ++ it "should return the class's name as its name" do ++ terminus.name.should == :freedom + end + +- it "should set the subclass's type to the abstract terminus name" do +- @terminus.terminus_type.should == :abstract ++ it "should return the class's indirection as its indirection" do ++ terminus.indirection.should equal indirection + end + +- it "should set the subclass's name to the indirection name" do +- @terminus.name.should == :term_type ++ it "should set the instances's type to the abstract terminus type's name" do ++ terminus.terminus_type.should == :code + end + +- it "should set the subclass's model to the indirection model" do +- @indirection.expects(:model).returns :yay +- @terminus.model.should == :yay ++ it "should set the instances's model to the indirection's model" do ++ terminus.model.should equal indirection.model + end + end + +- describe Puppet::Indirector::Terminus, " when a terminus instance" do ++ describe "when managing terminus classes" do ++ it "should provide a method for registering terminus classes" do ++ Puppet::Indirector::Terminus.should respond_to(:register_terminus_class) ++ end + +- it "should return the class's name as its name" do +- @terminus.name.should == :term_type ++ it "should provide a method for returning terminus classes by name and type" do ++ terminus = stub 'terminus_type', :name => :abstract, :indirection_name => :whatever ++ Puppet::Indirector::Terminus.register_terminus_class(terminus) ++ Puppet::Indirector::Terminus.terminus_class(:whatever, :abstract).should equal(terminus) + end + +- it "should return the class's indirection as its indirection" do +- @terminus.indirection.should equal(@indirection) ++ it "should set up autoloading for any terminus class types requested" do ++ Puppet::Indirector::Terminus.expects(:instance_load).with(:test2, "puppet/indirector/test2") ++ Puppet::Indirector::Terminus.terminus_class(:test2, :whatever) + end + +- it "should set the instances's type to the abstract terminus type's name" do +- @terminus.terminus_type.should == :abstract ++ it "should load terminus classes that are not found" do ++ # Set up instance loading; it would normally happen automatically ++ Puppet::Indirector::Terminus.instance_load :test1, "puppet/indirector/test1" ++ ++ Puppet::Indirector::Terminus.instance_loader(:test1).expects(:load).with(:yay) ++ Puppet::Indirector::Terminus.terminus_class(:test1, :yay) + end + +- it "should set the instances's model to the indirection's model" do +- @indirection.expects(:model).returns :yay +- @terminus.model.should == :yay ++ it "should fail when no indirection can be found" do ++ Puppet::Indirector::Indirection.expects(:instance).with(:abstract_concept).returns(nil) ++ expect { ++ class Puppet::AbstractConcept::Physics < Puppet::Indirector::Code ++ end ++ }.to raise_error(ArgumentError, /Could not find indirection instance/) + end +- end +-end + +-# LAK: This could reasonably be in the Indirection instances, too. It doesn't make +-# a whole heckuva lot of difference, except that with the instance loading in +-# the Terminus base class, we have to have a check to see if we're already +-# instance-loading a given terminus class type. +-describe Puppet::Indirector::Terminus, " when managing terminus classes" do +- it "should provide a method for registering terminus classes" do +- Puppet::Indirector::Terminus.should respond_to(:register_terminus_class) +- end ++ it "should register the terminus class with the terminus base class" do ++ Puppet::Indirector::Terminus.expects(:register_terminus_class).with do |type| ++ type.indirection_name == :abstract_concept and type.name == :intellect ++ end + +- it "should provide a method for returning terminus classes by name and type" do +- terminus = stub 'terminus_type', :name => :abstract, :indirection_name => :whatever +- Puppet::Indirector::Terminus.register_terminus_class(terminus) +- Puppet::Indirector::Terminus.terminus_class(:whatever, :abstract).should equal(terminus) ++ begin ++ class Puppet::AbstractConcept::Intellect < Puppet::Indirector::Code ++ end ++ ensure ++ Puppet::AbstractConcept.send(:remove_const, :Intellect) rescue nil ++ end ++ end + end + +- it "should set up autoloading for any terminus class types requested" do +- Puppet::Indirector::Terminus.expects(:instance_load).with(:test2, "puppet/indirector/test2") +- Puppet::Indirector::Terminus.terminus_class(:test2, :whatever) +- end ++ describe "when parsing class constants for indirection and terminus names" do ++ before :each do ++ Puppet::Indirector::Terminus.stubs(:register_terminus_class) ++ end + +- it "should load terminus classes that are not found" do +- # Set up instance loading; it would normally happen automatically +- Puppet::Indirector::Terminus.instance_load :test1, "puppet/indirector/test1" ++ let :subclass do ++ subclass = mock 'subclass' ++ subclass.stubs(:to_s).returns("TestInd::OneTwo") ++ subclass.stubs(:mark_as_abstract_terminus) ++ subclass ++ end + +- Puppet::Indirector::Terminus.instance_loader(:test1).expects(:load).with(:yay) +- Puppet::Indirector::Terminus.terminus_class(:test1, :yay) +- end ++ it "should fail when anonymous classes are used" do ++ expect { ++ Puppet::Indirector::Terminus.inherited(Class.new) ++ }.to raise_error(Puppet::DevError, /Terminus subclasses must have associated constants/) ++ end + +- it "should fail when no indirection can be found", :'fails_on_ruby_1.9.2' => true do +- Puppet::Indirector::Indirection.expects(:instance).with(:my_indirection).returns(nil) ++ it "should use the last term in the constant for the terminus class name" do ++ subclass.expects(:name=).with(:one_two) ++ subclass.stubs(:indirection=) ++ Puppet::Indirector::Terminus.inherited(subclass) ++ end + +- @abstract_terminus = Class.new(Puppet::Indirector::Terminus) do +- def self.to_s +- "Abstract" +- end ++ it "should convert the terminus name to a downcased symbol" do ++ subclass.expects(:name=).with(:one_two) ++ subclass.stubs(:indirection=) ++ Puppet::Indirector::Terminus.inherited(subclass) + end +- proc { +- @terminus = Class.new(@abstract_terminus) do +- def self.to_s +- "MyIndirection::TestType" +- end +- end +- }.should raise_error(ArgumentError) +- end + +- it "should register the terminus class with the terminus base class", :'fails_on_ruby_1.9.2' => true do +- Puppet::Indirector::Terminus.expects(:register_terminus_class).with do |type| +- type.indirection_name == :my_indirection and type.name == :test_terminus ++ it "should use the second to last term in the constant for the indirection name" do ++ subclass.expects(:indirection=).with(:test_ind) ++ subclass.stubs(:name=) ++ subclass.stubs(:terminus_type=) ++ Puppet::Indirector::Memory.inherited(subclass) + end +- @indirection = stub 'indirection', :name => :my_indirection, :register_terminus_type => nil +- Puppet::Indirector::Indirection.expects(:instance).with(:my_indirection).returns(@indirection) + +- @abstract_terminus = Class.new(Puppet::Indirector::Terminus) do +- def self.to_s +- "Abstract" +- end ++ it "should convert the indirection name to a downcased symbol" do ++ subclass.expects(:indirection=).with(:test_ind) ++ subclass.stubs(:name=) ++ subclass.stubs(:terminus_type=) ++ Puppet::Indirector::Memory.inherited(subclass) + end + +- @terminus = Class.new(@abstract_terminus) do +- def self.to_s +- "MyIndirection::TestTerminus" +- end ++ it "should convert camel case to lower case with underscores as word separators" do ++ subclass.expects(:name=).with(:one_two) ++ subclass.stubs(:indirection=) ++ ++ Puppet::Indirector::Terminus.inherited(subclass) + end + end +-end + +-describe Puppet::Indirector::Terminus, " when parsing class constants for indirection and terminus names" do +- before do +- @subclass = mock 'subclass' +- @subclass.stubs(:to_s).returns("TestInd::OneTwo") +- @subclass.stubs(:mark_as_abstract_terminus) +- Puppet::Indirector::Terminus.stubs(:register_terminus_class) +- end ++ describe "when creating terminus class types" do ++ before :all do ++ Puppet::Indirector::Terminus.stubs(:register_terminus_class) + +- it "should fail when anonymous classes are used" do +- proc { Puppet::Indirector::Terminus.inherited(Class.new) }.should raise_error(Puppet::DevError) +- end ++ class Puppet::Indirector::Terminus::TestTerminusType < Puppet::Indirector::Terminus ++ end ++ end + +- it "should use the last term in the constant for the terminus class name" do +- @subclass.expects(:name=).with(:one_two) +- @subclass.stubs(:indirection=) +- Puppet::Indirector::Terminus.inherited(@subclass) +- end ++ after :all do ++ Puppet::Indirector::Terminus.send(:remove_const, :TestTerminusType) ++ end + +- it "should convert the terminus name to a downcased symbol" do +- @subclass.expects(:name=).with(:one_two) +- @subclass.stubs(:indirection=) +- Puppet::Indirector::Terminus.inherited(@subclass) +- end ++ let :subclass do ++ Puppet::Indirector::Terminus::TestTerminusType ++ end ++ ++ it "should set the name of the abstract subclass to be its class constant" do ++ subclass.name.should == :test_terminus_type ++ end ++ ++ it "should mark abstract terminus types as such" do ++ subclass.should be_abstract_terminus ++ end + +- it "should use the second to last term in the constant for the indirection name" do +- @subclass.expects(:indirection=).with(:test_ind) +- @subclass.stubs(:name=) +- @subclass.stubs(:terminus_type=) +- Puppet::Indirector::Memory.inherited(@subclass) ++ it "should not allow instances of abstract subclasses to be created" do ++ expect { subclass.new }.to raise_error(Puppet::DevError) ++ end + end + +- it "should convert the indirection name to a downcased symbol" do +- @subclass.expects(:indirection=).with(:test_ind) +- @subclass.stubs(:name=) +- @subclass.stubs(:terminus_type=) +- Puppet::Indirector::Memory.inherited(@subclass) ++ describe "when listing terminus classes" do ++ it "should list the terminus files available to load" do ++ Puppet::Util::Autoload.any_instance.stubs(:files_to_load).returns ["/foo/bar/baz", "/max/runs/marathon"] ++ Puppet::Indirector::Terminus.terminus_classes('my_stuff').should == [:baz, :marathon] ++ end + end + +- it "should convert camel case to lower case with underscores as word separators" do +- @subclass.expects(:name=).with(:one_two) +- @subclass.stubs(:indirection=) ++ describe "when validating a request" do ++ let :request do ++ Puppet::Indirector::Request.new(indirection.name, :find, "the_key", instance) ++ end + +- Puppet::Indirector::Terminus.inherited(@subclass) +- end +-end ++ describe "`instance.name` does not match the key in the request" do ++ let(:instance) { model.new("wrong_key") } + +-describe Puppet::Indirector::Terminus, " when creating terminus class types", :'fails_on_ruby_1.9.2' => true do +- before do +- Puppet::Indirector::Terminus.stubs(:register_terminus_class) +- @subclass = Class.new(Puppet::Indirector::Terminus) do +- def self.to_s +- "Puppet::Indirector::Terminus::MyTermType" ++ it "raises an error " do ++ expect { ++ terminus.validate(request) ++ }.to raise_error( ++ Puppet::Indirector::ValidationError, ++ /Instance name .* does not match requested key/ ++ ) + end + end +- end + +- it "should set the name of the abstract subclass to be its class constant" do +- @subclass.name.should equal(:my_term_type) +- end ++ describe "`instance` is not an instance of the model class" do ++ let(:instance) { mock "instance", :name => "the_key" } + +- it "should mark abstract terminus types as such" do +- @subclass.should be_abstract_terminus +- end ++ it "raises an error" do ++ expect { ++ terminus.validate(request) ++ }.to raise_error( ++ Puppet::Indirector::ValidationError, ++ /Invalid instance type/ ++ ) ++ end ++ end + +- it "should not allow instances of abstract subclasses to be created" do +- proc { @subclass.new }.should raise_error(Puppet::DevError) +- end +-end ++ describe "the instance key and class match the request key and model class" do ++ let(:instance) { model.new("the_key") } + +-describe Puppet::Indirector::Terminus, " when listing terminus classes" do +- it "should list the terminus files available to load" do +- Puppet::Util::Autoload.any_instance.stubs(:files_to_load).returns ["/foo/bar/baz", "/max/runs/marathon"] +- Puppet::Indirector::Terminus.terminus_classes('my_stuff').should == [:baz, :marathon] ++ it "passes" do ++ terminus.validate(request) ++ end ++ end + end + end +Index: puppet-2.7.11/spec/unit/network/formats_spec.rb +=================================================================== +--- puppet-2.7.11.orig/spec/unit/network/formats_spec.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/spec/unit/network/formats_spec.rb 2013-03-11 12:32:08.867427541 -0400 +@@ -55,15 +55,15 @@ + @yaml.render_multiple(instances).should == "foo" + end + +- it "should intern by calling 'YAML.load'" do ++ it "should safely load YAML when interning" do + text = "foo" +- YAML.expects(:load).with("foo").returns "bar" ++ YAML.expects(:safely_load).with("foo").returns "bar" + @yaml.intern(String, text).should == "bar" + end + +- it "should intern multiples by calling 'YAML.load'" do ++ it "should safely load YAML when interning multiples" do + text = "foo" +- YAML.expects(:load).with("foo").returns "bar" ++ YAML.expects(:safely_load).with("foo").returns "bar" + @yaml.intern_multiple(String, text).should == "bar" + end + end +@@ -120,10 +120,10 @@ + @yaml.intern_multiple(String, text).should == "bar" + end + +- it "should decode by base64 decoding, uncompressing and Yaml loading" do ++ it "should decode by base64 decoding, uncompressing and safely Yaml loading" do + Base64.expects(:decode64).with("zorg").returns "foo" + Zlib::Inflate.expects(:inflate).with("foo").returns "baz" +- YAML.expects(:load).with("baz").returns "bar" ++ YAML.expects(:safely_load).with("baz").returns "bar" + @yaml.decode("zorg").should == "bar" + end + +Index: puppet-2.7.11/spec/unit/network/http/handler_spec.rb +=================================================================== +--- puppet-2.7.11.orig/spec/unit/network/http/handler_spec.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/spec/unit/network/http/handler_spec.rb 2013-03-11 12:32:08.867427541 -0400 +@@ -125,6 +125,31 @@ + @handler.request_format(@request).should == "s" + end + ++ it "should deserialize YAML parameters" do ++ params = {'my_param' => [1,2,3].to_yaml} ++ ++ decoded_params = @handler.send(:decode_params, params) ++ ++ decoded_params.should == {:my_param => [1,2,3]} ++ end ++ ++ it "should accept YAML parameters with !ruby/hash tags on Ruby 1.8", :if => RUBY_VERSION =~ /^1\.8/ do ++ params = {'my_param' => "--- !ruby/hash:Array {}"} ++ ++ decoded_params = @handler.send(:decode_params, params) ++ ++ decoded_params[:my_param].should be_an(Array) ++ end ++ ++ # These are only dangerous with Psych, which is Ruby 1.9-only. Since ++ # there's no real way to change the yamler in Puppet, assume that 1.9 means ++ # Psych, especially in tests. ++ it "should fail if YAML parameters have !ruby/hash tags on Ruby 1.9", :unless => RUBY_VERSION =~ /^1\.8/ do ++ params = {'my_param' => "--- !ruby/hash:Array {}"} ++ ++ expect { @handler.send(:decode_params, params) }.to raise_error(ArgumentError, /Illegal YAML mapping found/) ++ end ++ + describe "when finding a model instance" do + before do + @indirection.stubs(:find).returns @result +Index: puppet-2.7.11/spec/unit/network/http/rack/rest_spec.rb +=================================================================== +--- puppet-2.7.11.orig/spec/unit/network/http/rack/rest_spec.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/spec/unit/network/http/rack/rest_spec.rb 2013-03-11 12:32:08.867427541 -0400 +@@ -91,6 +91,23 @@ + @handler.set_response(@response, @file, 200) + end + end ++ ++ it "should ensure the body has been read on success" do ++ req = mk_req('/production/report/foo', :method => 'PUT') ++ req.body.expects(:read).at_least_once ++ ++ Puppet::Transaction::Report.stubs(:save) ++ ++ @handler.process(req, @response) ++ end ++ ++ it "should ensure the body has been partially read on failure" do ++ req = mk_req('/production/report/foo') ++ req.body.expects(:read).with(1) ++ req.stubs(:check_authorization).raises(Exception) ++ ++ @handler.process(req, @response) ++ end + end + + describe "and determining the request parameters" do +Index: puppet-2.7.11/spec/unit/network/http/webrick_spec.rb +=================================================================== +--- puppet-2.7.11.orig/spec/unit/network/http/webrick_spec.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/spec/unit/network/http/webrick_spec.rb 2013-03-11 12:32:08.867427541 -0400 +@@ -316,6 +316,10 @@ + @server.setup_ssl[:SSLEnable].should be_true + end + ++ it "should reject SSLv2" do ++ @server.setup_ssl[:SSLOptions].should == OpenSSL::SSL::OP_NO_SSLv2 ++ end ++ + it "should configure the verification method as 'OpenSSL::SSL::VERIFY_PEER'" do + @server.setup_ssl[:SSLVerifyClient].should == OpenSSL::SSL::VERIFY_PEER + end +Index: puppet-2.7.11/spec/unit/network/http_pool_spec.rb +=================================================================== +--- puppet-2.7.11.orig/spec/unit/network/http_pool_spec.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/spec/unit/network/http_pool_spec.rb 2013-03-11 12:32:08.867427541 -0400 +@@ -14,122 +14,119 @@ + end + + describe "when managing http instances" do +- def stub_settings(settings) +- settings.each do |param, value| +- Puppet.settings.stubs(:value).with(param).returns(value) +- end +- end +- +- before do ++ before :each do + # All of the cert stuff is tested elsewhere + Puppet::Network::HttpPool.stubs(:cert_setup) + end + + it "should return an http instance created with the passed host and port" do +- http = stub 'http', :use_ssl= => nil, :read_timeout= => nil, :open_timeout= => nil, :started? => false +- Net::HTTP.expects(:new).with("me", 54321, nil, nil).returns(http) +- Puppet::Network::HttpPool.http_instance("me", 54321).should equal(http) ++ http = Puppet::Network::HttpPool.http_instance("me", 54321) ++ http.should be_an_instance_of Net::HTTP ++ http.address.should == 'me' ++ http.port.should == 54321 + end + + it "should enable ssl on the http instance" do +- Puppet::Network::HttpPool.http_instance("me", 54321).instance_variable_get("@use_ssl").should be_true ++ Puppet::Network::HttpPool.http_instance("me", 54321).should be_use_ssl + end + +- it "should set the read timeout" do +- Puppet::Network::HttpPool.http_instance("me", 54321).read_timeout.should == 120 ++ context "proxy and timeout settings should propagate" do ++ subject { Puppet::Network::HttpPool.http_instance("me", 54321) } + end + +- it "should set the open timeout" do +- Puppet::Network::HttpPool.http_instance("me", 54321).open_timeout.should == 120 +- end +- +- it "should create the http instance with the proxy host and port set if the http_proxy is not set to 'none'" do +- stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 +- Puppet::Network::HttpPool.http_instance("me", 54321).open_timeout.should == 120 ++ it "should not set a proxy if the value is 'none'" do ++ Puppet[:http_proxy_host] = 'none' ++ Puppet::Network::HttpPool.http_instance("me", 54321).proxy_address.should be_nil + end + + it "should not cache http instances" do +- stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 ++ Puppet[:http_proxy_host] = "myhost" ++ Puppet[:http_proxy_port] = 432 ++ Puppet[:configtimeout] = 120 + old = Puppet::Network::HttpPool.http_instance("me", 54321) + Puppet::Network::HttpPool.http_instance("me", 54321).should_not equal(old) + end + end + +- describe "when adding certificate information to http instances" do +- before do +- @http = mock 'http' +- [:cert_store=, :verify_mode=, :ca_file=, :cert=, :key=].each { |m| @http.stubs(m) } +- @store = stub 'store' +- +- @cert = stub 'cert', :content => "real_cert" +- @key = stub 'key', :content => "real_key" +- @host = stub 'host', :certificate => @cert, :key => @key, :ssl_store => @store +- +- Puppet[:confdir] = "/sometthing/else" +- Puppet.settings.stubs(:value).returns "/some/file" +- Puppet.settings.stubs(:value).with(:hostcert).returns "/host/cert" +- Puppet.settings.stubs(:value).with(:localcacert).returns "/local/ca/cert" +- +- FileTest.stubs(:exist?).with("/host/cert").returns true +- FileTest.stubs(:exist?).with("/local/ca/cert").returns true +- +- Puppet::Network::HttpPool.stubs(:ssl_host).returns @host +- end +- +- after do +- Puppet.settings.clear +- end +- +- it "should do nothing if no host certificate is on disk" do +- FileTest.expects(:exist?).with("/host/cert").returns false +- @http.expects(:cert=).never +- Puppet::Network::HttpPool.cert_setup(@http) +- end +- +- it "should do nothing if no local certificate is on disk" do +- FileTest.expects(:exist?).with("/local/ca/cert").returns false +- @http.expects(:cert=).never +- Puppet::Network::HttpPool.cert_setup(@http) +- end +- +- it "should add a certificate store from the ssl host" do +- @http.expects(:cert_store=).with(@store) +- +- Puppet::Network::HttpPool.cert_setup(@http) +- end +- +- it "should add the client certificate" do +- @http.expects(:cert=).with("real_cert") ++ describe "when doing SSL setup for http instances" do ++ let :http do ++ http = Net::HTTP.new('localhost', 443) ++ http.use_ssl = true ++ http ++ end ++ ++ let :store do stub('store') end ++ ++ before :each do ++ Puppet[:hostcert] = '/host/cert' ++ Puppet[:localcacert] = '/local/ca/cert' ++ cert = stub 'cert', :content => 'real_cert' ++ key = stub 'key', :content => 'real_key' ++ host = stub 'host', :certificate => cert, :key => key, :ssl_store => store ++ Puppet::Network::HttpPool.stubs(:ssl_host).returns(host) ++ end ++ ++ shared_examples "HTTPS setup without all certificates" do ++ subject { Puppet::Network::HttpPool.cert_setup(http); http } ++ ++ it { should be_use_ssl } ++ its(:cert) { should be_nil } ++ its(:ca_file) { should be_nil } ++ its(:key) { should be_nil } ++ its(:verify_mode) { should == OpenSSL::SSL::VERIFY_NONE } ++ end ++ ++ context "with neither a host cert or a local CA cert" do ++ before :each do ++ FileTest.stubs(:exist?).with(Puppet[:hostcert]).returns(false) ++ FileTest.stubs(:exist?).with(Puppet[:localcacert]).returns(false) ++ end + +- Puppet::Network::HttpPool.cert_setup(@http) ++ include_examples "HTTPS setup without all certificates" + end + +- it "should add the client key" do +- @http.expects(:key=).with("real_key") ++ context "with there is no host certificate" do ++ before :each do ++ FileTest.stubs(:exist?).with(Puppet[:hostcert]).returns(false) ++ FileTest.stubs(:exist?).with(Puppet[:localcacert]).returns(true) ++ end + +- Puppet::Network::HttpPool.cert_setup(@http) ++ include_examples "HTTPS setup without all certificates" + end + +- it "should set the verify mode to OpenSSL::SSL::VERIFY_PEER" do +- @http.expects(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER) ++ context "with there is no local CA certificate" do ++ before :each do ++ FileTest.stubs(:exist?).with(Puppet[:hostcert]).returns(true) ++ FileTest.stubs(:exist?).with(Puppet[:localcacert]).returns(false) ++ end + +- Puppet::Network::HttpPool.cert_setup(@http) ++ include_examples "HTTPS setup without all certificates" + end + +- it "should set the ca file" do +- Puppet.settings.stubs(:value).returns "/some/file" +- FileTest.stubs(:exist?).with(Puppet[:hostcert]).returns true ++ context "with both the host and CA cert" do ++ subject { Puppet::Network::HttpPool.cert_setup(http); http } + +- Puppet.settings.stubs(:value).with(:localcacert).returns "/ca/cert/file" +- FileTest.stubs(:exist?).with("/ca/cert/file").returns true +- @http.expects(:ca_file=).with("/ca/cert/file") ++ before :each do ++ FileTest.expects(:exist?).with(Puppet[:hostcert]).returns(true) ++ FileTest.expects(:exist?).with(Puppet[:localcacert]).returns(true) ++ end + +- Puppet::Network::HttpPool.cert_setup(@http) ++ it { should be_use_ssl } ++ its(:cert_store) { should equal store } ++ its(:cert) { should == "real_cert" } ++ its(:key) { should == "real_key" } ++ its(:verify_mode) { should == OpenSSL::SSL::VERIFY_PEER } ++ its(:ca_file) { should == Puppet[:localcacert] } + end + + it "should set up certificate information when creating http instances" do +- Puppet::Network::HttpPool.expects(:cert_setup).with { |i| i.is_a?(Net::HTTP) } +- Puppet::Network::HttpPool.http_instance("one", "two") ++ Puppet::Network::HttpPool.expects(:cert_setup).with do |http| ++ http.should be_an_instance_of Net::HTTP ++ http.address.should == "one" ++ http.port.should == 2 ++ end ++ ++ Puppet::Network::HttpPool.http_instance("one", 2) + end + end + end +Index: puppet-2.7.11/spec/unit/network/rest_authconfig_spec.rb +=================================================================== +--- puppet-2.7.11.orig/spec/unit/network/rest_authconfig_spec.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/spec/unit/network/rest_authconfig_spec.rb 2013-03-11 12:32:08.867427541 -0400 +@@ -85,6 +85,9 @@ + end + + it "should create default ACL entries if no file have been read" do ++ # The singleton instance is stored as an instance variable we don't have ++ # access to, so.. instance_variable_set. Alas. ++ Puppet::Network::RestAuthConfig.instance_variable_set(:@main, nil) + Puppet::Network::RestAuthConfig.any_instance.stubs(:exists?).returns(false) + + Puppet::Network::RestAuthConfig.any_instance.expects(:insert_default_acl) +@@ -122,6 +125,18 @@ + @authconfig.insert_default_acl + end + +- end ++ it '(CVE-2013-2275) allows report submission only for the node matching the certname by default' do ++ acl = { ++ :acl => "~ ^\/report\/([^\/]+)$", ++ :method => :save, ++ :allow => '$1', ++ :authenticated => true ++ } ++ @authconfig.rights.stubs(:[]).returns(true) ++ @authconfig.rights.stubs(:[]).with(acl[:acl]).returns(nil) + ++ @authconfig.expects(:mk_acl).with(acl) ++ @authconfig.insert_default_acl ++ end ++ end + end +Index: puppet-2.7.11/spec/unit/parser/functions/inline_template_spec.rb +=================================================================== +--- puppet-2.7.11.orig/spec/unit/parser/functions/inline_template_spec.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/spec/unit/parser/functions/inline_template_spec.rb 2013-03-11 12:32:08.867427541 -0400 +@@ -58,4 +58,17 @@ + lambda { @scope.function_inline_template("1") }.should raise_error(Puppet::ParseError) + end + ++ it "is not interfered with by a variable called 'string' (#14093)" do ++ @scope.setvar("string", "this is a variable") ++ inline_template("this is a template").should == "this is a template" ++ end ++ ++ it "has access to a variable called 'string' (#14093)" do ++ @scope.setvar('string', "this is a variable") ++ inline_template("string was: <%= @string %>").should == "string was: this is a variable" ++ end ++ ++ def inline_template(*templates) ++ @scope.function_inline_template(templates) ++ end + end +Index: puppet-2.7.11/spec/unit/parser/functions/template_spec.rb +=================================================================== +--- puppet-2.7.11.orig/spec/unit/parser/functions/template_spec.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/spec/unit/parser/functions/template_spec.rb 2013-03-11 12:32:08.867427541 -0400 +@@ -53,6 +53,22 @@ + @scope.function_template(["1","2"]).should == "result1result2" + end + ++ it "is not interfered with by having a variable named 'string' (#14093)" do ++ @scope.setvar('string', "this output should not be seen") ++ @scope.stubs(:compiler => stub(:environment => 'production')) ++ @scope.stubs(:known_resource_types => stub(:watch_file)) ++ ++ eval_template("some text that is static").should == "some text that is static" ++ end ++ ++ it "has access to a variable named 'string' (#14093)" do ++ @scope.setvar('string', "the string value") ++ @scope.stubs(:compiler => stub(:environment => 'production')) ++ @scope.stubs(:known_resource_types => stub(:watch_file)) ++ ++ eval_template("string was: <%= @string %>").should == "string was: the string value" ++ end ++ + it "should raise an error if the template raises an error" do + tw = stub_everything 'template_wrapper' + Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw) +@@ -61,4 +77,9 @@ + lambda { @scope.function_template("1") }.should raise_error(Puppet::ParseError) + end + ++ def eval_template(content) ++ File.stubs(:read).with("template").returns(content) ++ Puppet::Parser::Files.stubs(:find_template).returns("template") ++ @scope.function_template(['template']) ++ end + end +Index: puppet-2.7.11/spec/unit/parser/templatewrapper_spec.rb +=================================================================== +--- puppet-2.7.11.orig/spec/unit/parser/templatewrapper_spec.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/spec/unit/parser/templatewrapper_spec.rb 2013-03-11 12:32:08.871427541 -0400 +@@ -30,16 +30,14 @@ + + it "should check template file existance and read its content" do + Puppet::Parser::Files.expects(:find_template).with("fake_template", @scope.environment.to_s).returns("/tmp/fake_template") +- File.expects(:read).with("/tmp/fake_template").returns("template content") + + @tw.file = @file + end + + it "should mark the file for watching" do +- Puppet::Parser::Files.expects(:find_template).returns("/tmp/fake_template") +- File.stubs(:read) ++ full_file_name = given_a_template_file("fake_template", "content") + +- @known_resource_types.expects(:watch_file).with("/tmp/fake_template") ++ @known_resource_types.expects(:watch_file).with(full_file_name) + @tw.file = @file + end + +@@ -66,6 +64,13 @@ + @tw.result.should eql("woot!") + end + ++ it "provides access to the name of the template via #file" do ++ full_file_name = given_a_template_file("fake_template", "<%= file %>") ++ ++ @tw.file = "fake_template" ++ @tw.result.should == full_file_name ++ end ++ + it "should return the processed template contents with a call to result and a string" do + mock_template + @tw.result("template contents").should eql("woot!") +@@ -139,4 +144,14 @@ + @tw.instance_variable_get("@one_").should == "foo" + end + end ++ ++ def given_a_template_file(name, contents) ++ full_name = "/full/path/to/#{name}" ++ Puppet::Parser::Files.stubs(:find_template). ++ with(name, anything()). ++ returns(full_name) ++ File.stubs(:read).with(full_name).returns(contents) ++ ++ full_name ++ end + end +Index: puppet-2.7.11/spec/unit/ssl/certificate_request_spec.rb +=================================================================== +--- puppet-2.7.11.orig/spec/unit/ssl/certificate_request_spec.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/spec/unit/ssl/certificate_request_spec.rb 2013-03-11 12:32:08.871427541 -0400 +@@ -254,6 +254,7 @@ + + csr = Puppet::SSL::CertificateRequest.new("me") + terminus = mock 'terminus' ++ terminus.stubs(:validate) + Puppet::SSL::CertificateRequest.indirection.expects(:prepare).returns(terminus) + terminus.expects(:save).with { |request| request.instance == csr && request.key == "me" } + +@@ -267,6 +268,7 @@ + + csr = Puppet::SSL::CertificateRequest.new("me") + terminus = mock 'terminus' ++ terminus.stubs(:validate) + Puppet::SSL::CertificateRequest.indirection.expects(:prepare).returns(terminus) + terminus.expects(:save).with { |request| request.instance == csr && request.key == "me" } + +Index: puppet-2.7.11/spec/unit/ssl/host_spec.rb +=================================================================== +--- puppet-2.7.11.orig/spec/unit/ssl/host_spec.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/spec/unit/ssl/host_spec.rb 2013-03-11 12:32:08.871427541 -0400 +@@ -489,6 +489,7 @@ + @request.stubs(:generate) + @request.stubs(:name).returns("myname") + terminus = stub 'terminus' ++ terminus.stubs(:validate) + Puppet::SSL::CertificateRequest.indirection.expects(:prepare).returns(terminus) + terminus.expects(:save).with { |req| req.instance == @request && req.key == "myname" }.raises "eh" + +Index: puppet-2.7.11/spec/unit/util/monkey_patches_spec.rb +=================================================================== +--- puppet-2.7.11.orig/spec/unit/util/monkey_patches_spec.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/spec/unit/util/monkey_patches_spec.rb 2013-03-11 12:32:08.871427541 -0400 +@@ -53,3 +53,15 @@ + [1,2,3,4].combination(3).to_a.should == [[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]] + end + end ++ ++describe OpenSSL::SSL::SSLContext do ++ it 'disables SSLv2 via the SSLContext#options bitmask' do ++ (subject.options & OpenSSL::SSL::OP_NO_SSLv2).should == OpenSSL::SSL::OP_NO_SSLv2 ++ end ++ it 'has no ciphers with version SSLv2 enabled' do ++ ciphers = subject.ciphers.select do |name, version, bits, alg_bits| ++ /SSLv2/.match(version) ++ end ++ ciphers.should be_empty ++ end ++end +Index: puppet-2.7.11/test/language/snippets.rb +=================================================================== +--- puppet-2.7.11.orig/test/language/snippets.rb 2013-03-11 12:32:08.875427541 -0400 ++++ puppet-2.7.11/test/language/snippets.rb 2013-03-11 12:32:08.871427541 -0400 +@@ -495,7 +495,7 @@ + + catalog = nil + assert_nothing_raised("Could not compile catalog") { +- catalog = Puppet::Resource::Catalog.indirection.find(node) ++ catalog = Puppet::Resource::Catalog.indirection.find(node.name) + } + + assert_nothing_raised("Could not convert catalog") { +Index: puppet-2.7.11/lib/puppet/indirector/file_bucket_file/selector.rb +=================================================================== +--- puppet-2.7.11.orig/lib/puppet/indirector/file_bucket_file/selector.rb 2013-03-11 12:32:08.815427539 -0400 ++++ puppet-2.7.11/lib/puppet/indirector/file_bucket_file/selector.rb 2013-03-12 11:09:37.397513030 -0400 +@@ -44,6 +44,10 @@ + true + end + end ++ ++ def validate_key(request) ++ get_terminus(request).validate(request) ++ end + end + end + diff -Nru puppet-2.7.11/debian/patches/series puppet-2.7.11/debian/patches/series --- puppet-2.7.11/debian/patches/series 2012-07-06 12:59:36.000000000 +0000 +++ puppet-2.7.11/debian/patches/series 2013-03-11 16:31:38.000000000 +0000 @@ -3,3 +3,4 @@ puppet-12844 CVE-2012-1906_CVE-2012-1986_to_CVE-2012-1989.patch 2.7.17-Puppet-July-2012-CVE-fixes.patch +security-mar-2013.patch