diff --git a/lib/puppet/reports/store.rb b/lib/puppet/reports/store.rb index 4f2d22532..21dfa2d25 100644 --- a/lib/puppet/reports/store.rb +++ b/lib/puppet/reports/store.rb @@ -1,74 +1,74 @@ require 'puppet' require 'fileutils' require 'tempfile' SEPARATOR = [Regexp.escape(File::SEPARATOR.to_s), Regexp.escape(File::ALT_SEPARATOR.to_s)].join Puppet::Reports.register_report(:store) do desc "Store the yaml report on disk. Each host sends its report as a YAML dump and this just stores the file on disk, in the `reportdir` directory. These files collect quickly -- one every half hour -- so it is a good idea to perform some maintenance on them if you use this report (it's the only default report)." def process - # We don't want any tracking back in the fs. Unlikely, but there - # you go. - if host =~ Regexp.union(/[#{SEPARATOR}]/, /\A\.\.?\Z/) - raise ArgumentError, "Invalid node name #{host.inspect}" - end + validate_host(host) dir = File.join(Puppet[:reportdir], host) if ! FileTest.exists?(dir) FileUtils.mkdir_p(dir) FileUtils.chmod_R(0750, dir) end # Now store the report. now = Time.now.gmtime name = %w{year month day hour min}.collect do |method| # Make sure we're at least two digits everywhere "%02d" % now.send(method).to_s end.join("") + ".yaml" file = File.join(dir, name) f = Tempfile.new(name, dir) begin begin f.chmod(0640) f.print to_yaml ensure f.close end FileUtils.mv(f.path, file) rescue => detail puts detail.backtrace if Puppet[:trace] Puppet.warning "Could not write report for #{host} at #{file}: #{detail}" end # Only testing cares about the return value file end # removes all reports for a given host? def self.destroy(host) - if host =~ Regexp.union(/[#{SEPARATOR}]/, /\A\.\.?\Z/) - raise ArgumentError, "Invalid node name #{host.inspect}" - end + validate_host(host) dir = File.join(Puppet[:reportdir], host) if File.exists?(dir) Dir.entries(dir).each do |file| next if ['.','..'].include?(file) file = File.join(dir, file) File.unlink(file) if File.file?(file) end Dir.rmdir(dir) end end -end + def validate_host(host) + if host =~ Regexp.union(/[#{SEPARATOR}]/, /\A\.\.?\Z/) + raise ArgumentError, "Invalid node name #{host.inspect}" + end + end + module_function :validate_host +end diff --git a/spec/unit/reports/store_spec.rb b/spec/unit/reports/store_spec.rb index b4f849cde..f149eff5e 100755 --- a/spec/unit/reports/store_spec.rb +++ b/spec/unit/reports/store_spec.rb @@ -1,76 +1,76 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/reports' require 'time' require 'pathname' require 'tempfile' require 'fileutils' processor = Puppet::Reports.report(:store) describe processor do describe "#process" do include PuppetSpec::Files before :each do Puppet[:reportdir] = File.join(tmpdir('reports'), 'reports') @report = YAML.load_file(File.join(PuppetSpec::FIXTURE_DIR, 'yaml/report2.6.x.yaml')).extend processor end it "should create a report directory for the client if one doesn't exist" do @report.process File.should be_directory(File.join(Puppet[:reportdir], @report.host)) end it "should write the report to the file in YAML" do Time.stubs(:now).returns(Time.parse("2011-01-06 12:00:00 UTC")) @report.process File.read(File.join(Puppet[:reportdir], @report.host, "201101061200.yaml")).should == @report.to_yaml end it "should write to the report directory in the correct sequence" do # By doing things in this sequence we should protect against race # conditions Time.stubs(:now).returns(Time.parse("2011-01-06 12:00:00 UTC")) writeseq = sequence("write") file = mock "file" Tempfile.expects(:new).in_sequence(writeseq).returns(file) file.expects(:chmod).in_sequence(writeseq).with(0640) file.expects(:print).with(@report.to_yaml).in_sequence(writeseq) file.expects(:close).in_sequence(writeseq) file.stubs(:path).returns(File.join(Dir.tmpdir, "foo123")) FileUtils.expects(:mv).in_sequence(writeseq).with(File.join(Dir.tmpdir, "foo123"), File.join(Puppet[:reportdir], @report.host, "201101061200.yaml")) @report.process end - ['..', 'hello/', '/hello', 'he/llo', 'hello/..', '.'].each do |node| - it "rejects #{node.inspect}" do - @report.host = node - expect { @report.process }.to raise_error(ArgumentError, /Invalid node/) - end + it "rejects invalid hostnames" do + @report.host = ".." + FileTest.expects(:exists?).never + Tempfile.expects(:new).never + expect { @report.process }.to raise_error(ArgumentError, /Invalid node/) end + end - ['.hello', 'hello.', '..hi', 'hi..'].each do |node| - it "accepts #{node.inspect}" do - @report.host = node - @report.process - end + describe "::destroy" do + it "rejects invalid hostnames" do + File.expects(:unlink).never + expect { processor.destroy("..") }.to raise_error(ArgumentError, /Invalid node/) end end - describe "::destroy" do + describe "::validate_host" do ['..', 'hello/', '/hello', 'he/llo', 'hello/..', '.'].each do |node| it "rejects #{node.inspect}" do - expect { processor.destroy(node) }.to raise_error(ArgumentError, /Invalid node/) + expect { processor.validate_host(node) }.to raise_error(ArgumentError, /Invalid node/) end end ['.hello', 'hello.', '..hi', 'hi..'].each do |node| it "accepts #{node.inspect}" do - processor.destroy(node) + processor.validate_host(node) end end end end