summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaire McQuin <mcquin@users.noreply.github.com>2014-09-24 15:15:19 -0700
committerClaire McQuin <mcquin@users.noreply.github.com>2014-09-24 15:15:19 -0700
commit72575c3c46c89b9c74d63baceaaf2237ed9e0e6f (patch)
tree8f4dc355badd3c08c312db2357a8811ad66d1fc5
parentbed2af024937259b93bb6955ee794cf70e20693f (diff)
parent07527f98c1d2aeb2e60711721d581426961cc761 (diff)
downloadchef-72575c3c46c89b9c74d63baceaaf2237ed9e0e6f.tar.gz
Merge pull request #2092 from opscode/mcquin/escape_glob
Escape file paths for globbing
-rw-r--r--CHANGELOG.md1
-rw-r--r--DOC_CHANGES.md19
-rw-r--r--lib/chef/cookbook/cookbook_version_loader.rb7
-rw-r--r--lib/chef/cookbook/syntax_check.rb5
-rw-r--r--lib/chef/cookbook_loader.rb2
-rw-r--r--lib/chef/data_bag.rb4
-rw-r--r--lib/chef/deprecation/provider/file.rb3
-rw-r--r--lib/chef/file_cache.rb5
-rw-r--r--lib/chef/http/ssl_policies.rb3
-rw-r--r--lib/chef/knife/core/bootstrap_context.rb4
-rw-r--r--lib/chef/knife/core/object_loader.rb10
-rw-r--r--lib/chef/knife/core/subcommand_loader.rb11
-rw-r--r--lib/chef/knife/data_bag_from_file.rb3
-rw-r--r--lib/chef/provider/deploy.rb3
-rw-r--r--lib/chef/provider/deploy/revision.rb4
-rw-r--r--lib/chef/provider/dsc_script.rb1
-rw-r--r--lib/chef/provider/link.rb1
-rw-r--r--lib/chef/provider/package/freebsd/pkg.rb3
-rw-r--r--lib/chef/provider/package/portage.rb5
-rw-r--r--lib/chef/provider/remote_directory.rb3
-rw-r--r--lib/chef/provider/service/gentoo.rb3
-rw-r--r--lib/chef/provider/service/insserv.rb3
-rw-r--r--lib/chef/provider/service/macosx.rb3
-rw-r--r--lib/chef/provider/user/dscl.rb5
-rw-r--r--lib/chef/role.rb2
-rw-r--r--lib/chef/util/backup.rb5
-rw-r--r--lib/chef/util/path_helper.rb8
-rw-r--r--spec/functional/util/path_helper_spec.rb37
-rw-r--r--spec/unit/knife/environment_from_file_spec.rb4
-rw-r--r--spec/unit/util/path_helper_spec.rb24
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