summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Keiser <jkeiser@opscode.com>2013-10-15 20:41:52 -0700
committerJohn Keiser <jkeiser@opscode.com>2013-10-15 20:41:52 -0700
commite1c6bef68597f5c4f418107ef9e3638e44cc8cf7 (patch)
treec6871e3dcded48265f2b82c26ab13cd62a0ba2be
parent2da5abd0648f3ba6977d3a69d858078237bee585 (diff)
downloadchef-e1c6bef68597f5c4f418107ef9e3638e44cc8cf7.tar.gz
Add --config-file-jail to avoid loading user knife.rb in tests
-rw-r--r--lib/chef/application.rb51
-rw-r--r--lib/chef/application/client.rb5
-rw-r--r--lib/chef/chef_fs/config.rb10
-rw-r--r--lib/chef/chef_fs/path_utils.rb5
-rw-r--r--lib/chef/config.rb9
-rw-r--r--lib/chef/knife.rb12
-rw-r--r--spec/integration/client/client_spec.rb47
-rw-r--r--spec/unit/application_spec.rb5
-rw-r--r--spec/unit/knife/config_file_selection_spec.rb17
9 files changed, 113 insertions, 48 deletions
diff --git a/lib/chef/application.rb b/lib/chef/application.rb
index ca9cfff291..2987f58839 100644
--- a/lib/chef/application.rb
+++ b/lib/chef/application.rb
@@ -25,6 +25,8 @@ require 'chef/platform'
require 'mixlib/cli'
require 'tmpdir'
require 'rbconfig'
+require 'pathname'
+require 'chef/chef_fs/path_utils'
class Chef::Application
include Mixlib::CLI
@@ -75,7 +77,21 @@ class Chef::Application
# Parse the config file
def load_config_file
if !config[:config_file]
- Chef::Log.warn("No config file found or specified on command line, not loading.")
+ Chef::Log.warn("No config file found or specified on command line, using command line options.")
+ Chef::Config.merge!(config)
+ return
+ end
+
+ if !Chef::Application.config_file_exists?(config[:config_file])
+ Chef::Log.warn("*****************************************")
+ if !File.exists?(config[:config_file])
+ Chef::Log.warn("Did not find config file: #{config[:config_file]}, using command line options.")
+ else
+ Chef::Log.warn("Config file #{config[:config_file]} not inside the jail #{Chef::Config.config_file_jail}, using command line options.", 2)
+ end
+ Chef::Log.warn("*****************************************")
+
+ Chef::Config.merge!(config)
return
end
@@ -86,12 +102,6 @@ class Chef::Application
else
::File::open(config[:config_file]) { |f| apply_config(f.path) }
end
- rescue Errno::ENOENT => error
- Chef::Log.warn("*****************************************")
- Chef::Log.warn("Did not find config file: #{config[:config_file]}, using command line options.")
- Chef::Log.warn("*****************************************")
-
- Chef::Config.merge!(config)
rescue SocketError => error
Chef::Application.fatal!("Error getting config file #{config[:config_file]}", 2)
rescue Chef::Exceptions::ConfigurationError => error
@@ -101,6 +111,33 @@ class Chef::Application
end
end
+ # Determines whether a config file exists, taking into account file existence
+ # as well as Chef::Config.config_file_jail (if they are not under the jail,
+ # this method will return false). Takes symlinks and relative paths into
+ # account.
+ def self.config_file_exists?(config_file)
+ if config_file =~ /^(http|https):\/\//
+ return true
+ end
+
+ begin
+ real_config_file = Pathname.new(config_file).realpath.to_s
+ rescue Errno::ENOENT
+ return false
+ end
+
+ return true if !Chef::Config.config_file_jail
+
+ begin
+ jail = Pathname.new(Chef::Config.config_file_jail).realpath.to_s
+ rescue Errno::ENOENT
+ Chef::Log.warn("Config file jail #{Chef::Config.config_file_jail} does not exist: will not load any config file.")
+ return false
+ end
+
+ Chef::ChefFS::PathUtils.descendant_of?(real_config_file, jail)
+ end
+
# Initialize and configure the logger.
# === Loggers and Formatters
# In Chef 10.x and previous, the Logger was the primary/only way that Chef
diff --git a/lib/chef/application/client.rb b/lib/chef/application/client.rb
index ea8d599b4c..0964101fad 100644
--- a/lib/chef/application/client.rb
+++ b/lib/chef/application/client.rb
@@ -206,6 +206,10 @@ class Chef::Application::Client < Chef::Application
:long => "--chef-zero-port PORT",
:description => "Port to start chef-zero on"
+ option :config_file_jail,
+ :long => "--config-file-jail PATH",
+ :description => "Directory under which config files are allowed to be loaded (no client.rb or knife.rb outside this path will be loaded)."
+
if Chef::Platform.windows?
option :fatal_windows_admin_check,
:short => "-A",
@@ -272,6 +276,7 @@ class Chef::Application::Client < Chef::Application
end
def load_config_file
+ Chef::Config.config_file_jail = config[:config_file_jail] if config[:config_file_jail]
if !config.has_key?(:config_file)
if config[:local_mode]
require 'chef/knife'
diff --git a/lib/chef/chef_fs/config.rb b/lib/chef/chef_fs/config.rb
index bfbe379775..e08b976961 100644
--- a/lib/chef/chef_fs/config.rb
+++ b/lib/chef/chef_fs/config.rb
@@ -84,16 +84,14 @@ class Chef
# If the path does not reach into ANY specified directory, nil is returned.
def server_path(file_path)
pwd = File.expand_path(Dir.pwd)
- absolute_path = Chef::ChefFS::PathUtils.realest_path(File.expand_path(file_path, pwd))
+ absolute_pwd = Chef::ChefFS::PathUtils.realest_path(File.expand_path(file_path, pwd))
# Check all object paths (cookbooks_dir, data_bags_dir, etc.)
object_paths.each_pair do |name, paths|
paths.each do |path|
realest_path = Chef::ChefFS::PathUtils.realest_path(path)
- if absolute_path[0,realest_path.length] == realest_path &&
- (absolute_path.length == realest_path.length ||
- absolute_path[realest_path.length,1] =~ /#{PathUtils.regexp_path_separator}/)
- relative_path = Chef::ChefFS::PathUtils::relative_to(absolute_path, realest_path)
+ if PathUtils.descendant_of?(absolute_pwd, realest_path)
+ relative_path = Chef::ChefFS::PathUtils::relative_to(absolute_pwd, realest_path)
return relative_path == '.' ? "/#{name}" : "/#{name}/#{relative_path}"
end
end
@@ -102,7 +100,7 @@ class Chef
# Check chef_repo_path
Array(@chef_config[:chef_repo_path]).flatten.each do |chef_repo_path|
realest_chef_repo_path = Chef::ChefFS::PathUtils.realest_path(chef_repo_path)
- if absolute_path == realest_chef_repo_path
+ if absolute_pwd == realest_chef_repo_path
return '/'
end
end
diff --git a/lib/chef/chef_fs/path_utils.rb b/lib/chef/chef_fs/path_utils.rb
index 805b092b3a..9ef75ce2e5 100644
--- a/lib/chef/chef_fs/path_utils.rb
+++ b/lib/chef/chef_fs/path_utils.rb
@@ -82,6 +82,11 @@ class Chef
end
end
+ def self.descendant_of?(path, ancestor)
+ path[0,ancestor.length] == ancestor &&
+ (ancestor.length == path.length || path[ancestor.length,1] =~ /#{PathUtils.regexp_path_separator}/)
+ end
+
def self.is_absolute?(path)
path =~ /^#{regexp_path_separator}/
end
diff --git a/lib/chef/config.rb b/lib/chef/config.rb
index 0a4ee6e94c..f2610f01e2 100644
--- a/lib/chef/config.rb
+++ b/lib/chef/config.rb
@@ -73,6 +73,13 @@ class Chef
formatters << [name, file_path]
end
+ # Config file to load (client.rb, knife.rb, etc. defaults set differently in knife, chef-client, etc.)
+ configurable(:config_file)
+
+ # No config file (client.rb / knife.rb / etc.) will be loaded outside this path.
+ # Major use case is tests, where we don't want to load the user's config files.
+ configurable(:config_file_jail)
+
default :formatters, []
# Override the config dispatch to set the value of multiple server options simultaneously
@@ -215,6 +222,8 @@ class Chef
# An array of paths to search for knife exec scripts if they aren't in the current directory
default :script_path, []
+ # The root of all caches (checksums, cache and backup). If local mode is on,
+ # this is under the user's home directory.
default(:cache_path) do
if local_mode
"#{user_home}#{platform_path_separator}.chef#{platform_path_separator}local-mode-cache"
diff --git a/lib/chef/knife.rb b/lib/chef/knife.rb
index 5ff6b92643..dbb719c30a 100644
--- a/lib/chef/knife.rb
+++ b/lib/chef/knife.rb
@@ -335,8 +335,7 @@ class Chef
end
candidate_configs.each do | candidate_config |
- candidate_config = File.expand_path(candidate_config)
- if File.exist?(candidate_config)
+ if Chef::Application.config_file_exists?(candidate_config)
return candidate_config
end
end
@@ -403,12 +402,17 @@ class Chef
end
def configure_chef
- unless config[:config_file]
+ if config[:config_file]
+ if !Chef::Application.config_file_exists?(config[:config_file])
+ ui.error("Specified config file #{config[:config_file]} does not exist#{Chef::Config.config_file_jail ? " or is not under config file jail #{Chef::Config.config_file_jail}" : ""}!")
+ exit 1
+ end
+ else
located_config_file = self.class.locate_config_file
config[:config_file] = located_config_file if located_config_file
end
- # Don't try to load a knife.rb if it doesn't exist.
+ # Don't try to load a knife.rb if it wasn't specified.
if config[:config_file]
Chef::Log.debug("Using configuration from #{config[:config_file]}")
read_config_file(config[:config_file])
diff --git a/spec/integration/client/client_spec.rb b/spec/integration/client/client_spec.rb
index a91c5966a1..839899d059 100644
--- a/spec/integration/client/client_spec.rb
+++ b/spec/integration/client/client_spec.rb
@@ -19,29 +19,40 @@ EOM
result.error!
end
- it 'should complete with success when cwd is just above cookbooks and paths are not specified' do
- chef_dir = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "bin"))
- result = shell_out("#{chef_dir}/chef-client -z -o 'x::default'", :cwd => path_to(''))
- result.error!
- end
+ context 'and no config file' do
+ it 'should complete with success when cwd is just above cookbooks and paths are not specified' do
+ chef_dir = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "bin"))
+ result = shell_out("#{chef_dir}/chef-client -z -o 'x::default' --config-file-jail \"#{path_to('')}\"", :cwd => path_to(''))
+ result.error!
+ end
- it 'should complete with success when cwd is below cookbooks and paths are not specified' do
- chef_dir = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "bin"))
- result = shell_out("#{chef_dir}/chef-client -z -o 'x::default'", :cwd => path_to('cookbooks/x'))
- result.error!
- end
+ it 'should complete with success when cwd is below cookbooks and paths are not specified' do
+ chef_dir = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "bin"))
+ result = shell_out("#{chef_dir}/chef-client -z -o 'x::default' --config-file-jail \"#{path_to('')}\"", :cwd => path_to('cookbooks/x'))
+ result.error!
+ end
- it 'should fail when cwd is below high above and paths are not specified' do
- chef_dir = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "bin"))
- result = shell_out("#{chef_dir}/chef-client -z -o 'x::default'", :cwd => File.expand_path('..', path_to('')))
- result.exitstatus.should == 1
+ it 'should fail when cwd is below high above and paths are not specified' do
+ chef_dir = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "bin"))
+ result = shell_out("#{chef_dir}/chef-client -z -o 'x::default' --config-file-jail \"#{path_to('')}\"", :cwd => File.expand_path('..', path_to('')))
+ result.exitstatus.should == 1
+ end
end
- it 'should load .chef/knife.rb when -z is specified' do
+ context 'and a config file under .chef/knife.rb' do
file '.chef/knife.rb', 'xxx.xxx'
- chef_dir = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "bin"))
- result = shell_out("#{chef_dir}/chef-client -z -o 'x::default'", :cwd => path_to(''))
- result.exitstatus.should == 2
+
+ it 'should load .chef/knife.rb when -z is specified' do
+ chef_dir = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "bin"))
+ result = shell_out("#{chef_dir}/chef-client -z -o 'x::default' --config-file-jail \"#{path_to('')}\"", :cwd => path_to(''))
+ result.exitstatus.should == 2
+ end
+
+ it 'fails to load .chef/knife.rb when -z is specified and --config-file-jail does not include the .chef/knife.rb' do
+ chef_dir = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "bin"))
+ result = shell_out("#{chef_dir}/chef-client -z -o 'x::default' --config-file-jail \"#{path_to('roles')}\"", :cwd => path_to(''))
+ result.error!
+ end
end
it "should complete with success" do
diff --git a/spec/unit/application_spec.rb b/spec/unit/application_spec.rb
index 9d473bbefb..e27a5cddb8 100644
--- a/spec/unit/application_spec.rb
+++ b/spec/unit/application_spec.rb
@@ -108,12 +108,14 @@ describe Chef::Application do
end
it "should configure chef::config from a file" do
+ Chef::Application.should_receive(:config_file_exists?).with('/etc/chef/default.rb').and_return(true)
File.should_receive(:open).with("/etc/chef/default.rb").and_yield(@config_file)
Chef::Config.should_receive(:from_file).with(@config_file.path)
@app.configure_chef
end
it "should merge the local config hash into chef::config" do
+ Chef::Application.should_receive(:config_file_exists?).with('/etc/chef/default.rb').and_return(true)
File.should_receive(:open).with("/etc/chef/default.rb").and_yield(@config_file)
@app.configure_chef
Chef::Config.rspec_ran.should == "true"
@@ -128,7 +130,7 @@ describe Chef::Application do
it "should emit a warning" do
Chef::Config.should_not_receive(:from_file).with("/etc/chef/default.rb")
- Chef::Log.should_receive(:warn).with("No config file found or specified on command line, not loading.")
+ Chef::Log.should_receive(:warn).with("No config file found or specified on command line, using command line options.")
@app.configure_chef
end
end
@@ -164,6 +166,7 @@ describe Chef::Application do
after {@config_file.unlink}
it "should configure chef::config from an URL" do
+ Chef::Application.should_receive(:config_file_exists?).with('http://example.com/foo.rb').and_call_original
Chef::REST.should_receive(:new).with("", nil, nil).at_least(1).times.and_return(@rest)
@rest.should_receive(:fetch).with("http://example.com/foo.rb").and_yield(@config_file)
@app.configure_chef
diff --git a/spec/unit/knife/config_file_selection_spec.rb b/spec/unit/knife/config_file_selection_spec.rb
index 33aaddbf48..0aa77392f3 100644
--- a/spec/unit/knife/config_file_selection_spec.rb
+++ b/spec/unit/knife/config_file_selection_spec.rb
@@ -38,6 +38,7 @@ describe Chef::Knife do
env_config = File.expand_path(File.join(Dir.tmpdir, 'knife.rb'))
File.stub!(:exist?).and_return(false)
File.stub!(:exist?).with(env_config).and_return(true)
+ Chef::Application.stub(:config_file_exists?) { |arg| arg == env_config }
ENV['KNIFE_HOME'] = Dir.tmpdir
@knife = Chef::Knife.new
@@ -47,9 +48,7 @@ describe Chef::Knife do
it "configure knife from PWD" do
pwd_config = "#{Dir.pwd}/knife.rb"
- File.stub!(:exist?).and_return do | arg |
- [ pwd_config ].include? arg
- end
+ Chef::Application.stub(:config_file_exists?) { |arg| arg == pwd_config }
@knife = Chef::Knife.new
@knife.configure_chef
@@ -59,9 +58,7 @@ describe Chef::Knife do
it "configure knife from UPWARD" do
upward_dir = File.expand_path "#{Dir.pwd}/.chef"
upward_config = File.expand_path "#{upward_dir}/knife.rb"
- File.stub!(:exist?).and_return do | arg |
- [ upward_config ].include? arg
- end
+ Chef::Application.stub(:config_file_exists?) { |arg| arg == upward_config }
Chef::Knife.stub!(:chef_config_dir).and_return(upward_dir)
@knife = Chef::Knife.new
@@ -71,9 +68,7 @@ describe Chef::Knife do
it "configure knife from HOME" do
home_config = File.expand_path(File.join("#{ENV['HOME']}", "/.chef/knife.rb"))
- File.stub!(:exist?).and_return do | arg |
- [ home_config ].include? arg
- end
+ Chef::Application.stub(:config_file_exists?) { |arg| arg == home_config }
@knife = Chef::Knife.new
@knife.configure_chef
@@ -95,9 +90,7 @@ describe Chef::Knife do
upward_config = File.expand_path "#{upward_dir}/knife.rb"
home_config = File.expand_path(File.join("#{ENV['HOME']}", "/.chef/knife.rb"))
configs = [ env_config, pwd_config, upward_config, home_config ]
- File.stub!(:exist?).and_return do | arg |
- configs.include? arg
- end
+ Chef::Application.stub(:config_file_exists?) { |arg| configs.include?(arg) }
Chef::Knife.stub!(:chef_config_dir).and_return(upward_dir)
ENV['KNIFE_HOME'] = Dir.tmpdir