diff options
author | Claire McQuin <mcquin@users.noreply.github.com> | 2014-09-24 15:15:19 -0700 |
---|---|---|
committer | Claire McQuin <mcquin@users.noreply.github.com> | 2014-09-24 15:15:19 -0700 |
commit | 72575c3c46c89b9c74d63baceaaf2237ed9e0e6f (patch) | |
tree | 8f4dc355badd3c08c312db2357a8811ad66d1fc5 | |
parent | bed2af024937259b93bb6955ee794cf70e20693f (diff) | |
parent | 07527f98c1d2aeb2e60711721d581426961cc761 (diff) | |
download | chef-72575c3c46c89b9c74d63baceaaf2237ed9e0e6f.tar.gz |
Merge pull request #2092 from opscode/mcquin/escape_glob
Escape file paths for globbing
30 files changed, 150 insertions, 41 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 9826e96b13..fe182840e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -135,6 +135,7 @@ * Transfer trusted certificates under :trusted_certs_dir during bootstrap. * Set :ssl_verify_mode to :verify_peer by default. * Add homebrew provider for package resource, use it by default on OS X (Issue #1709) +* Add escape_glob method to PathHelper, update glob operations. * Verify x509 properties of certificates in the :trusted_certs_dir during knife ssl check. ## Last Release: 11.14.2 diff --git a/DOC_CHANGES.md b/DOC_CHANGES.md index 736dc4f8a5..29fca72484 100644 --- a/DOC_CHANGES.md +++ b/DOC_CHANGES.md @@ -394,3 +394,22 @@ end # the `:immediate` is required for results to be defined. notifies :reboot_now, "reboot[now]", :immediate ``` + +### Escape sensitive characters before globbing +Some paths contain characters reserved by glob and must be escaped so that +glob operations perform as expected. One common example is Windows file paths +separated by `"\\"`. To ensure that your globs work correctly, it is recommended +that you apply `Chef::Util::PathHelper::escape_glob` before globbing file paths. + +```ruby +path = "C:\\Users\\me\\chef-repo\\cookbooks" +Dir.exist?(path) # true +Dir.entries(path) # [".", "..", "apache2", "apt", ...] + +Dir.glob(File.join(path, "*")) # [] +Dir[File.join(path, "*")] # [] + +PathHelper = Chef::Util::PathHelper +Dir.glob(File.join(PathHelper.escape_glob(path), "*")) # ["#{path}\\apache2", "#{path}\\apt", ...] +Dir[PathHelper.escape_glob(path) + "/*"] # ["#{path}\\apache2", "#{path}\\apt", ...] +``` diff --git a/lib/chef/cookbook/cookbook_version_loader.rb b/lib/chef/cookbook/cookbook_version_loader.rb index 47258c4d4e..5c9de6b8ca 100644 --- a/lib/chef/cookbook/cookbook_version_loader.rb +++ b/lib/chef/cookbook/cookbook_version_loader.rb @@ -2,6 +2,7 @@ require 'chef/cookbook_version' require 'chef/cookbook/chefignore' require 'chef/cookbook/metadata' +require 'chef/util/path_helper' class Chef class Cookbook @@ -212,7 +213,7 @@ class Chef end def load_root_files - Dir.glob(File.join(cookbook_path, '*'), File::FNM_DOTMATCH).each do |file| + Dir.glob(File.join(Chef::Util::PathHelper.escape_glob(cookbook_path), '*'), File::FNM_DOTMATCH).each do |file| next if File.directory?(file) next if File.basename(file) == UPLOADED_COOKBOOK_VERSION_FILE cookbook_settings[:root_filenames][file[@relative_path, 1]] = file @@ -220,7 +221,7 @@ class Chef end def load_recursively_as(category, category_dir, glob) - file_spec = File.join(cookbook_path, category_dir, '**', glob) + file_spec = File.join(Chef::Util::PathHelper.escape_glob(cookbook_path, category_dir), '**', glob) Dir.glob(file_spec, File::FNM_DOTMATCH).each do |file| next if File.directory?(file) cookbook_settings[category][file[@relative_path, 1]] = file @@ -228,7 +229,7 @@ class Chef end def load_as(category, *path_glob) - Dir[File.join(cookbook_path, *path_glob)].each do |file| + Dir[File.join(Chef::Util::PathHelper.escape_glob(cookbook_path), *path_glob)].each do |file| cookbook_settings[category][file[@relative_path, 1]] = file end end diff --git a/lib/chef/cookbook/syntax_check.rb b/lib/chef/cookbook/syntax_check.rb index 092fb47eba..3b35f77de9 100644 --- a/lib/chef/cookbook/syntax_check.rb +++ b/lib/chef/cookbook/syntax_check.rb @@ -21,6 +21,7 @@ require 'stringio' require 'erubis' require 'chef/mixin/shell_out' require 'chef/mixin/checksum' +require 'chef/util/path_helper' class Chef class Cookbook @@ -110,7 +111,7 @@ class Chef end def ruby_files - remove_ignored_files Dir[File.join(cookbook_path, '**', '*.rb')] + remove_ignored_files Dir[File.join(Chef::Util::PathHelper.escape_glob(cookbook_path), '**', '*.rb')] end def untested_ruby_files @@ -125,7 +126,7 @@ class Chef end def template_files - remove_ignored_files Dir[File.join(cookbook_path, '**/templates/**', '*.erb')] + remove_ignored_files Dir[File.join(Chef::Util::PathHelper.escape_glob(cookbook_path), '**/templates/**', '*.erb')] end def untested_template_files diff --git a/lib/chef/cookbook_loader.rb b/lib/chef/cookbook_loader.rb index d569cdd008..c05fedb141 100644 --- a/lib/chef/cookbook_loader.rb +++ b/lib/chef/cookbook_loader.rb @@ -161,7 +161,7 @@ class Chef @all_files_in_repo_paths ||= begin @repo_paths.inject([]) do |all_children, repo_path| - all_children += Dir[File.join(repo_path, "*")] + all_children += Dir[File.join(Chef::Util::PathHelper.escape_glob(repo_path), "*")] end end end diff --git a/lib/chef/data_bag.rb b/lib/chef/data_bag.rb index 99808d0609..528be3f2c4 100644 --- a/lib/chef/data_bag.rb +++ b/lib/chef/data_bag.rb @@ -90,7 +90,7 @@ class Chef raise Chef::Exceptions::InvalidDataBagPath, "Data bag path '#{path}' is invalid" end - names += Dir.glob(File.join(path, "*")).map{|f|File.basename(f)}.sort + names += Dir.glob(File.join(Chef::Util::PathHelper.escape_glob(path), "*")).map{|f|File.basename(f)}.sort end names.inject({}) {|h, n| h[n] = n; h} else @@ -116,7 +116,7 @@ class Chef raise Chef::Exceptions::InvalidDataBagPath, "Data bag path '#{path}' is invalid" end - Dir.glob(File.join(path, name.to_s, "*.json")).inject({}) do |bag, f| + Dir.glob(File.join(Chef::Util::PathHelper.escape_glob(path, name.to_s), "*.json")).inject({}) do |bag, f| item = Chef::JSONCompat.from_json(IO.read(f)) # Check if we have multiple items with similar names (ids) and raise if their content differs diff --git a/lib/chef/deprecation/provider/file.rb b/lib/chef/deprecation/provider/file.rb index 0e9105247c..125f31fe10 100644 --- a/lib/chef/deprecation/provider/file.rb +++ b/lib/chef/deprecation/provider/file.rb @@ -16,6 +16,7 @@ # limitations under the License. # +require 'chef/util/path_helper' class Chef module Deprecation @@ -180,7 +181,7 @@ class Chef # Clean up after the number of backups slice_number = @new_resource.backup - backup_files = Dir[::File.join(prefix, ".#{@new_resource.path}.chef-*")].sort { |a,b| b <=> a } + backup_files = Dir[Chef::Util::PathHelper.escape_glob(prefix, ".#{@new_resource.path}") + ".chef-*"].sort { |a,b| b <=> a } if backup_files.length >= @new_resource.backup remainder = backup_files.slice(slice_number..-1) remainder.each do |backup_to_delete| diff --git a/lib/chef/file_cache.rb b/lib/chef/file_cache.rb index 89e934ea05..c2f77bdff6 100644 --- a/lib/chef/file_cache.rb +++ b/lib/chef/file_cache.rb @@ -20,6 +20,7 @@ require 'chef/mixin/create_path' require 'chef/exceptions' require 'chef/json_compat' require 'fileutils' +require 'chef/util/path_helper' class Chef class FileCache @@ -157,9 +158,9 @@ class Chef # [String] - An array of file cache keys matching the glob def find(glob_pattern) keys = Array.new - Dir[File.join(file_cache_path, glob_pattern)].each do |f| + Dir[File.join(Chef::Util::PathHelper.escape_glob(file_cache_path), glob_pattern)].each do |f| if File.file?(f) - keys << f[/^#{Regexp.escape(Dir[file_cache_path].first) + File::Separator}(.+)/, 1] + keys << f[/^#{Regexp.escape(Dir[Chef::Util::PathHelper.escape_glob(file_cache_path)].first) + File::Separator}(.+)/, 1] end end keys diff --git a/lib/chef/http/ssl_policies.rb b/lib/chef/http/ssl_policies.rb index f2a9c5b845..9c180c154e 100644 --- a/lib/chef/http/ssl_policies.rb +++ b/lib/chef/http/ssl_policies.rb @@ -22,6 +22,7 @@ # require 'openssl' +require 'chef/util/path_helper' class Chef class HTTP @@ -76,7 +77,7 @@ class Chef http_client.cert_store.set_default_paths end if config.trusted_certs_dir - certs = Dir.glob(File.join(config.trusted_certs_dir, "*.{crt,pem}")) + certs = Dir.glob(File.join(Chef::Util::PathHelper.escape_glob(config.trusted_certs_dir), "*.{crt,pem}")) certs.each do |cert_file| cert = OpenSSL::X509::Certificate.new(File.read(cert_file)) add_trusted_cert(cert) diff --git a/lib/chef/knife/core/bootstrap_context.rb b/lib/chef/knife/core/bootstrap_context.rb index 87c25ca160..03e3a42e4a 100644 --- a/lib/chef/knife/core/bootstrap_context.rb +++ b/lib/chef/knife/core/bootstrap_context.rb @@ -17,6 +17,8 @@ # require 'chef/run_list' +require 'chef/util/path_helper' + class Chef class Knife module Core @@ -167,7 +169,7 @@ CONFIG def trusted_certs_content content = "" if @chef_config[:trusted_certs_dir] - Dir.glob(File.join(@chef_config[:trusted_certs_dir], "*.{crt,pem}")).each do |cert| + Dir.glob(File.join(Chef::Util::PathHelper.escape_glob(@chef_config[:trusted_certs_dir]), "*.{crt,pem}")).each do |cert| content << "cat > /etc/chef/trusted_certs/#{File.basename(cert)} <<'EOP'\n" + IO.read(File.expand_path(cert)) + "\nEOP\n" end diff --git a/lib/chef/knife/core/object_loader.rb b/lib/chef/knife/core/object_loader.rb index de683b23fd..209f6987d4 100644 --- a/lib/chef/knife/core/object_loader.rb +++ b/lib/chef/knife/core/object_loader.rb @@ -17,6 +17,7 @@ # require 'ffi_yajl' +require 'chef/util/path_helper' class Chef class Knife @@ -69,15 +70,15 @@ class Chef # # @api public def find_all_objects(path) - path = File.join(path, '*') + path = File.join(Chef::Util::PathHelper.escape_glob(File.expand_path(path)), '*') path << '.{json,rb}' - objects = Dir.glob(File.expand_path(path)) + objects = Dir.glob(path) objects.map { |o| File.basename(o) } end def find_all_object_dirs(path) - path = File.join(path, '*') - objects = Dir.glob(File.expand_path(path)) + path = File.join(Chef::Util::PathHelper.escape_glob(File.expand_path(path)), '*') + objects = Dir.glob(path) objects.delete_if { |o| !File.directory?(o) } objects.map { |o| File.basename(o) } end @@ -111,4 +112,3 @@ class Chef end end end - diff --git a/lib/chef/knife/core/subcommand_loader.rb b/lib/chef/knife/core/subcommand_loader.rb index 0489c726b3..d2be1be2d3 100644 --- a/lib/chef/knife/core/subcommand_loader.rb +++ b/lib/chef/knife/core/subcommand_loader.rb @@ -17,6 +17,7 @@ # require 'chef/version' +require 'chef/util/path_helper' class Chef class Knife class SubcommandLoader @@ -41,11 +42,11 @@ class Chef user_specific_files = [] if chef_config_dir - user_specific_files.concat Dir.glob(File.expand_path("plugins/knife/*.rb", chef_config_dir)) + user_specific_files.concat Dir.glob(File.expand_path("plugins/knife/*.rb", Chef::Util::PathHelper.escape_glob(chef_config_dir))) end # finally search ~/.chef/plugins/knife/*.rb - user_specific_files.concat Dir.glob(File.join(env['HOME'], '.chef', 'plugins', 'knife', '*.rb')) if env['HOME'] + user_specific_files.concat Dir.glob(File.join(Chef::Util::PathHelper.escape_glob(env['HOME'], '.chef', 'plugins', 'knife'), '*.rb')) if env['HOME'] user_specific_files end @@ -107,7 +108,7 @@ class Chef def find_subcommands_via_dirglob # The "require paths" of the core knife subcommands bundled with chef - files = Dir[File.expand_path('../../../knife/*.rb', __FILE__)] + files = Dir[File.join(Chef::Util::PathHelper.escape_glob(File.expand_path('../../../knife', __FILE__)), '*.rb')] subcommand_files = {} files.each do |knife_file| rel_path = knife_file[/#{CHEF_ROOT}#{Regexp.escape(File::SEPARATOR)}(.*)\.rb/,1] @@ -146,7 +147,7 @@ class Chef if check_load_path files = $LOAD_PATH.map { |load_path| - Dir["#{File.expand_path glob, load_path}#{Gem.suffix_pattern}"] + Dir["#{File.expand_path glob, Chef::Util::PathHelper.escape_glob(load_path)}#{Gem.suffix_pattern}"] }.flatten.select { |file| File.file? file.untaint } end @@ -180,7 +181,7 @@ class Chef spec.require_paths.first end - glob = File.join("#{spec.full_gem_path}/#{dirs}", glob) + glob = File.join(Chef::Util::PathHelper.escape_glob(spec.full_gem_path, dirs), glob) Dir[glob].map { |f| f.untaint } end diff --git a/lib/chef/knife/data_bag_from_file.rb b/lib/chef/knife/data_bag_from_file.rb index 4c90fe6c6c..2ff79b6256 100644 --- a/lib/chef/knife/data_bag_from_file.rb +++ b/lib/chef/knife/data_bag_from_file.rb @@ -18,6 +18,7 @@ # require 'chef/knife' +require 'chef/util/path_helper' class Chef class Knife @@ -126,7 +127,7 @@ class Chef paths = Array.new args.each do |path| if File.directory?(path) - paths.concat(Dir.glob(File.join(path, "*.json"))) + paths.concat(Dir.glob(File.join(Chef::Util::PathHelper.escape_glob(path), "*.json"))) else paths << path end diff --git a/lib/chef/provider/deploy.rb b/lib/chef/provider/deploy.rb index 426e69644e..db147278c2 100644 --- a/lib/chef/provider/deploy.rb +++ b/lib/chef/provider/deploy.rb @@ -22,6 +22,7 @@ require "chef/monkey_patches/fileutils" require "chef/provider/git" require "chef/provider/subversion" require "chef/dsl/recipe" +require "chef/util/path_helper" class Chef class Provider @@ -243,7 +244,7 @@ class Chef end def all_releases - Dir.glob(@new_resource.deploy_to + "/releases/*").sort + Dir.glob(Chef::Util::PathHelper.escape_glob(@new_resource.deploy_to) + "/releases/*").sort end def update_cached_repo diff --git a/lib/chef/provider/deploy/revision.rb b/lib/chef/provider/deploy/revision.rb index ed65742154..c98c1e5c75 100644 --- a/lib/chef/provider/deploy/revision.rb +++ b/lib/chef/provider/deploy/revision.rb @@ -42,7 +42,7 @@ class Chef known_releases = sorted_releases - Dir["#{new_resource.deploy_to}/releases/*"].each do |release_dir| + Dir["#{Chef::Util::PathHelper.escape_glob(new_resource.deploy_to)}/releases/*"].each do |release_dir| unless known_releases.include?(release_dir) converge_by("Remove unknown release in #{release_dir}") do FileUtils.rm_rf(release_dir) @@ -85,7 +85,7 @@ class Chef end def sorted_releases_from_filesystem - Dir.glob(new_resource.deploy_to + "/releases/*").sort_by { |d| ::File.ctime(d) } + Dir.glob(Chef::Util::PathHelper.escape_glob(new_resource.deploy_to) + "/releases/*").sort_by { |d| ::File.ctime(d) } end def load_cache diff --git a/lib/chef/provider/dsc_script.rb b/lib/chef/provider/dsc_script.rb index 5d7322842c..c979800cba 100644 --- a/lib/chef/provider/dsc_script.rb +++ b/lib/chef/provider/dsc_script.rb @@ -19,6 +19,7 @@ require 'chef/util/powershell/cmdlet' require 'chef/util/dsc/configuration_generator' require 'chef/util/dsc/local_configuration_manager' +require 'chef/util/path_helper' class Chef class Provider diff --git a/lib/chef/provider/link.rb b/lib/chef/provider/link.rb index af2fe4a84f..639dc4f3ff 100644 --- a/lib/chef/provider/link.rb +++ b/lib/chef/provider/link.rb @@ -22,6 +22,7 @@ require 'chef/mixin/file_class' require 'chef/resource/link' require 'chef/provider' require 'chef/scan_access_control' +require 'chef/util/path_helper' class Chef class Provider diff --git a/lib/chef/provider/package/freebsd/pkg.rb b/lib/chef/provider/package/freebsd/pkg.rb index c757d26fe5..ebbfbb19b4 100644 --- a/lib/chef/provider/package/freebsd/pkg.rb +++ b/lib/chef/provider/package/freebsd/pkg.rb @@ -20,6 +20,7 @@ # require 'chef/provider/package/freebsd/base' +require 'chef/util/path_helper' class Chef class Provider @@ -87,7 +88,7 @@ class Chef end def file_candidate_version_path - Dir["#{@new_resource.source}/#{@current_resource.package_name}*"][-1].to_s + Dir[Chef::Util::PathHelper.escape_glob("#{@new_resource.source}/#{@current_resource.package_name}") + "*"][-1].to_s end def file_candidate_version diff --git a/lib/chef/provider/package/portage.rb b/lib/chef/provider/package/portage.rb index 7e0eebd0d9..816e262ab0 100644 --- a/lib/chef/provider/package/portage.rb +++ b/lib/chef/provider/package/portage.rb @@ -19,6 +19,7 @@ require 'chef/provider/package' require 'chef/mixin/command' require 'chef/resource/package' +require 'chef/util/path_helper' class Chef class Provider @@ -34,7 +35,9 @@ class Chef category, pkg = %r{^#{PACKAGE_NAME_PATTERN}$}.match(@new_resource.package_name)[1,2] - possibilities = Dir["/var/db/pkg/#{category || "*"}/#{pkg}-*"].map {|d| d.sub(%r{/var/db/pkg/}, "") } + globsafe_category = category ? Chef::Util::PathHelper.escape_glob(category) : nil + globsafe_pkg = Chef::Util::PathHelper.escape_glob(pkg) + possibilities = Dir["/var/db/pkg/#{globsafe_category || "*"}/#{globsafe_pkg}-*"].map {|d| d.sub(%r{/var/db/pkg/}, "") } versions = possibilities.map do |entry| if(entry =~ %r{[^/]+/#{Regexp.escape(pkg)}\-(\d[\.\d]*((_(alpha|beta|pre|rc|p)\d*)*)?(-r\d+)?)}) [$&, $1] diff --git a/lib/chef/provider/remote_directory.rb b/lib/chef/provider/remote_directory.rb index 77e2754b08..553eb14ae5 100644 --- a/lib/chef/provider/remote_directory.rb +++ b/lib/chef/provider/remote_directory.rb @@ -26,6 +26,7 @@ require 'uri' require 'tempfile' require 'net/https' require 'set' +require 'chef/util/path_helper' class Chef class Provider @@ -35,7 +36,7 @@ class Chef def action_create super - files_to_purge = Set.new(Dir.glob(::File.join(@new_resource.path, '**', '*'), + files_to_purge = Set.new(Dir.glob(::File.join(Chef::Util::PathHelper.escape_glob(@new_resource.path), '**', '*'), ::File::FNM_DOTMATCH).select do |name| name !~ /(?:^|#{Regexp.escape(::File::SEPARATOR)})\.\.?$/ end) diff --git a/lib/chef/provider/service/gentoo.rb b/lib/chef/provider/service/gentoo.rb index a68abfebc9..e2dff10994 100644 --- a/lib/chef/provider/service/gentoo.rb +++ b/lib/chef/provider/service/gentoo.rb @@ -19,6 +19,7 @@ require 'chef/provider/service/init' require 'chef/mixin/command' +require 'chef/util/path_helper' class Chef::Provider::Service::Gentoo < Chef::Provider::Service::Init def load_current_resource @@ -29,7 +30,7 @@ class Chef::Provider::Service::Gentoo < Chef::Provider::Service::Init super @current_resource.enabled( - Dir.glob("/etc/runlevels/**/#{@current_resource.service_name}").any? do |file| + Dir.glob("/etc/runlevels/**/#{Chef::Util::PathHelper.escape_glob(@current_resource.service_name)}").any? do |file| @found_script = true exists = ::File.exists? file readable = ::File.readable? file diff --git a/lib/chef/provider/service/insserv.rb b/lib/chef/provider/service/insserv.rb index f4c85dd9d3..1ee817707a 100644 --- a/lib/chef/provider/service/insserv.rb +++ b/lib/chef/provider/service/insserv.rb @@ -17,6 +17,7 @@ # require 'chef/provider/service/init' +require 'chef/util/path_helper' class Chef class Provider @@ -27,7 +28,7 @@ class Chef super # Look for a /etc/rc.*/SnnSERVICE link to signifiy that the service would be started in a runlevel - if Dir.glob("/etc/rc**/S*#{@current_resource.service_name}").empty? + if Dir.glob("/etc/rc**/S*#{Chef::Util::PathHelper.escape_glob(@current_resource.service_name)}").empty? @current_resource.enabled false else @current_resource.enabled true diff --git a/lib/chef/provider/service/macosx.rb b/lib/chef/provider/service/macosx.rb index cf5e554559..ad1535327b 100644 --- a/lib/chef/provider/service/macosx.rb +++ b/lib/chef/provider/service/macosx.rb @@ -19,6 +19,7 @@ require 'rexml/document' require 'chef/resource/service' require 'chef/provider/service/simple' +require 'chef/util/path_helper' class Chef class Provider @@ -194,7 +195,7 @@ class Chef plists = PLIST_DIRS.inject([]) do |results, dir| edir = ::File.expand_path(dir) entries = Dir.glob( - "#{edir}/*#{@current_resource.service_name}*.plist" + "#{edir}/*#{Chef::Util::PathHelper.escape_glob(@current_resource.service_name)}*.plist" ) entries.any? ? results << entries : results end diff --git a/lib/chef/provider/user/dscl.rb b/lib/chef/provider/user/dscl.rb index a24a047596..84f5145c52 100644 --- a/lib/chef/provider/user/dscl.rb +++ b/lib/chef/provider/user/dscl.rb @@ -20,6 +20,7 @@ require 'mixlib/shellout' require 'chef/provider/user' require 'openssl' require 'plist' +require 'chef/util/path_helper' class Chef class Provider @@ -308,7 +309,7 @@ user password using shadow hash.") src = @current_resource.home FileUtils.mkdir_p(@new_resource.home) - files = ::Dir.glob("#{src}/*", ::File::FNM_DOTMATCH) - ["#{src}/.","#{src}/.."] + files = ::Dir.glob("#{Chef::Util::PathHelper.escape_glob(src)}/*", ::File::FNM_DOTMATCH) - ["#{src}/.","#{src}/.."] ::FileUtils.mv(files,@new_resource.home, :force => true) ::FileUtils.rmdir(src) ::FileUtils.chown_R(@new_resource.username,@new_resource.gid.to_s,@new_resource.home) @@ -555,7 +556,7 @@ user password using shadow hash.") user_info = nil # We flush the cache here in order to make sure that we read fresh information - # for the user. + # for the user. shell_out("dscacheutil '-flushcache'") begin diff --git a/lib/chef/role.rb b/lib/chef/role.rb index aeea873051..4b4669f4b5 100644 --- a/lib/chef/role.rb +++ b/lib/chef/role.rb @@ -235,7 +235,7 @@ class Chef def self.from_disk(name) paths = Array(Chef::Config[:role_path]) paths.each do |path| - roles_files = Dir.glob(File.join(path, "**", "**")) + roles_files = Dir.glob(File.join(Chef::Util::PathHelper.escape_glob(path), "**", "**")) js_files = roles_files.select { |file| file.match /\/#{name}\.json$/ } rb_files = roles_files.select { |file| file.match /\/#{name}\.rb$/ } if js_files.count > 1 or rb_files.count > 1 diff --git a/lib/chef/util/backup.rb b/lib/chef/util/backup.rb index 43e3434050..0cc009ca1f 100644 --- a/lib/chef/util/backup.rb +++ b/lib/chef/util/backup.rb @@ -16,6 +16,8 @@ # limitations under the License. # +require 'chef/util/path_helper' + class Chef class Util class Backup @@ -77,9 +79,8 @@ class Chef end def sorted_backup_files - Dir[::File.join(prefix, ".#{path}.chef-*")].sort { |a,b| b <=> a } + Dir[Chef::Util::PathHelper.escape_glob(prefix, ".#{path}") + ".chef-*"].sort { |a,b| b <=> a } end end end end - diff --git a/lib/chef/util/path_helper.rb b/lib/chef/util/path_helper.rb index e9fb4e7773..8ca8279593 100644 --- a/lib/chef/util/path_helper.rb +++ b/lib/chef/util/path_helper.rb @@ -133,6 +133,14 @@ class Chef def self.paths_eql?(path1, path2) canonical_path(path1) == canonical_path(path2) end + + # Paths which may contain glob-reserved characters need + # to be escaped before globbing can be done. + # http://stackoverflow.com/questions/14127343 + def self.escape_glob(*parts) + path = cleanpath(join(*parts)) + path.gsub(/[\\\{\}\[\]\*\?]/) { |x| "\\"+x } + end end end end diff --git a/spec/functional/util/path_helper_spec.rb b/spec/functional/util/path_helper_spec.rb new file mode 100644 index 0000000000..ccdf383c22 --- /dev/null +++ b/spec/functional/util/path_helper_spec.rb @@ -0,0 +1,37 @@ +# +# Copyright:: Copyright (c) 2014 Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'tmpdir' +require 'chef/util/path_helper' +require 'spec_helper' + +describe Chef::Util::PathHelper, "escape_glob" do + PathHelper = Chef::Util::PathHelper + + it "escapes the glob metacharacters so globbing succeeds" do + # make a dir + Dir.mktmpdir("\\silly[dir]") do |dir| + # add some files + files = ["some.rb", "file.txt", "names.csv"] + files.each do |file| + File.new(File.join(dir, file), 'w').close + end + + pattern = File.join(PathHelper.escape_glob(dir), "*") + Dir.glob(pattern).map { |x| File.basename(x) }.should match_array(files) + end + end +end diff --git a/spec/unit/knife/environment_from_file_spec.rb b/spec/unit/knife/environment_from_file_spec.rb index f208320c5d..ed3631fcf5 100644 --- a/spec/unit/knife/environment_from_file_spec.rb +++ b/spec/unit/knife/environment_from_file_spec.rb @@ -57,8 +57,8 @@ describe Chef::Knife::EnvironmentFromFile do end it "loads all environments with -a" do - File.stub(:expand_path).with("./environments/*.{json,rb}").and_return("/tmp/environments") - Dir.stub(:glob).with("/tmp/environments").and_return(["spec.rb", "apple.rb"]) + File.stub(:expand_path).with("./environments/").and_return("/tmp/environments") + Dir.stub(:glob).with("/tmp/environments/*.{json,rb}").and_return(["spec.rb", "apple.rb"]) @knife.name_args = [] @knife.stub(:config).and_return({:all => true}) @environment.should_receive(:save).twice diff --git a/spec/unit/util/path_helper_spec.rb b/spec/unit/util/path_helper_spec.rb index 66ad323c52..1d97efc607 100644 --- a/spec/unit/util/path_helper_spec.rb +++ b/spec/unit/util/path_helper_spec.rb @@ -214,6 +214,28 @@ describe Chef::Util::PathHelper do PathHelper.stub(:canonical_path).with("bandit").and_return("c:/Bo/Bandit") PathHelper.stub(:canonical_path).with("../bandit/bandit").and_return("c:/bandit/bandit") expect(PathHelper.paths_eql?("bandit", "../bandit/bandit")).to be_false - end + end + end + + describe "escape_glob" do + it "escapes characters reserved by glob" do + path = "C:\\this\\*path\\[needs]\\escaping?" + escaped_path = "C:\\\\this\\\\\\*path\\\\\\[needs\\]\\\\escaping\\?" + expect(PathHelper.escape_glob(path)).to eq(escaped_path) + end + + context "when given more than one argument" do + it "joins, cleanpaths, and escapes characters reserved by glob" do + args = ["this/*path", "[needs]", "escaping?"] + escaped_path = if windows? + "this\\\\\\*path\\\\\\[needs\\]\\\\escaping\\?" + else + "this/\\*path/\\[needs\\]/escaping\\?" + end + expect(PathHelper).to receive(:join).with(*args).and_call_original + expect(PathHelper).to receive(:cleanpath).and_call_original + expect(PathHelper.escape_glob(*args)).to eq(escaped_path) + end + end end end |