Daniel Pittman <[EMAIL PROTECTED]> writes:
> Luke Kanies <[EMAIL PROTECTED]> writes:
>> On Jul 16, 2008, at 9:49 AM, Daniel Pittman wrote:
>>> Luke Kanies <[EMAIL PROTECTED]> writes:
[...]
> So, as expected this had the side-effect of making it impossible to
> detect when an undefined variable was used in a template. This was
> quite a loss, in my opinion, and something I would rather not give up.
>
> So, I spent some more time investigating this and came up with the patch
> just sent to the list: it manually hides the Kernel methods, which are
> the only ones (as far as I can determine) that cause us this grief.
...and Google seems to be eating the patch, so here it is inline to this
email. It should be MIME clean, but let me know if it isn't.
Regards,
Daniel
>From 19309a8bb4b425538744aae09821aa23047f981a Mon Sep 17 00:00:00 2001
From: Daniel Pittman <[EMAIL PROTECTED]>
Date: Sun, 20 Jul 2008 18:44:01 +1000
Subject: [PATCH 1/1] Resolve Redmine #1427: When expanding ERB templates Puppet
variables are
shadowed by unqualified methods exported by Kernel, such as 'fork' or 'puts'.
* Hide all Kernel methods within the TemplateWrapper class.
* Update TemplateWrapper to qualify calls to Kernel#raise.
* Create a local alias for the Kernel#binding method, which does not work as
advertised if called through a qualified name.
* Implement a test to ensure that a range of dangerous Kernel methods can be
evaluated correctly within the template.
Signed-off-by: Daniel Pittman <[EMAIL PROTECTED]>
---
lib/puppet/parser/templatewrapper.rb | 43 ++++++++++++++++++++++++++++++----
test/language/functions.rb | 39 ++++++++++++++++++++++++++++++
2 files changed, 77 insertions(+), 5 deletions(-)
diff --git a/lib/puppet/parser/templatewrapper.rb
b/lib/puppet/parser/templatewrapper.rb
index 4790cea..201bc7c 100644
--- a/lib/puppet/parser/templatewrapper.rb
+++ b/lib/puppet/parser/templatewrapper.rb
@@ -1,17 +1,50 @@
# A simple wrapper for templates, so they don't have full access to
# the scope objects.
+#
+# WARNING: This code does not have all the regular Kernel methods available
+# unqualified. This is important for template variable processing, but *BAD*
+# if you (say) thought raise was syntax rather than a method.
class Puppet::Parser::TemplateWrapper
attr_accessor :scope, :file
include Puppet::Util
Puppet::Util.logmethods(self)
+ # Address Redmine #1427: the Kernel module exports a bunch of methods that
+ # are called if used as unqualified terms within any object. This,
+ # unfortunately, means that a Kernel method will shadow a puppet variable.
+ #
+ # We can work around that, though, by hiding those methods inside this
+ # object and ensuring that they act as if undefined -- which causes the
+ # evaluation inside ERB to work as expected by calling our method_missing.
+ #
+ # Since Ruby doesn't provide an 'instance_variable_missing' method we
+ # can't use that to capture these lookups. A pity that isn't there eh?
+
+ # Unfortunately, binding does not work as advertised if you qualify the
+ # name in any fashion, so we are forced to retain a local alias that is
+ # less likely to conflict with a valid puppet variable.
+ alias_method(:__binding__, :binding)
+
+ # Shadow all the Kernel methods we have so they act as if undefined.
+ # Only apply this to direct methods of kernel, not inherited methods.
+ Kernel.methods(false).each do |name|
+ begin
+ undef_method(name)
+ rescue
+ # Ignore the error and hope that nothing too disasterous happens.
+ # Module#method_added triggers an "undefined method" exception
+ # within Puppet, presumably because it is visible in Kernel but
+ # not to us.
+ end
+ end
+
def initialize(scope, file)
@scope = scope
@file = Puppet::Module::find_template(file,
@scope.compiler.environment)
unless FileTest.exists?(@file)
- raise Puppet::ParseError,
- "Could not find template %s" % file
+ Kernel.raise(Puppet::ParseError,
+ "Could not find template %s" % file)
end
# We'll only ever not have a parser in testing, but, eh.
@@ -40,8 +73,8 @@ class Puppet::Parser::TemplateWrapper
else
# Just throw an error immediately, instead of searching for
# other missingmethod things or whatever.
- raise Puppet::ParseError,
- "Could not find value for '%s'" % name
+ Kernel.raise(Puppet::ParseError,
+ "Could not find value for '%s'" % name)
end
end
@@ -49,7 +82,7 @@ class Puppet::Parser::TemplateWrapper
result = nil
benchmark(:debug, "Interpolated template [EMAIL PROTECTED]") do
template = ERB.new(File.read(@file), 0, "-")
- result = template.result(binding)
+ result = template.result(__binding__)
end
result
diff --git a/test/language/functions.rb b/test/language/functions.rb
index a5d52d7..f231207 100755
--- a/test/language/functions.rb
+++ b/test/language/functions.rb
@@ -279,6 +279,45 @@ class TestLangFunctions < Test::Unit::TestCase
end
end
+ # Make sure that we can use variable names that are identical to Kernel
+ # exported methods, such as 'fork', 'puts', 'format' or 'raise'
+ def test_singletemplates
+ template = tempfile()
+
+ # Test a variety of Kernel methods
+ %w(raise fork puts format binding chop eval exec).each do |word|
+ File.open(template, "w") do |f|
+ f.puts "template <%= #{word} %>"
+ end
+
+ func = nil
+ assert_nothing_raised do
+ func = Puppet::Parser::AST::Function.new(
+ :name => "template",
+ :ftype => :rvalue,
+ :arguments => AST::ASTArray.new(
+ :children => [stringobj(template)]
+ )
+ )
+ end
+ ast = varobj("output", func)
+
+ scope = mkscope
+ assert_raise(Puppet::ParseError) do
+ ast.evaluate(scope)
+ end
+
+ scope.setvar(word, "passes the test")
+
+ assert_nothing_raised do
+ ast.evaluate(scope)
+ end
+
+ assert_equal("template passes the test\n",
scope.lookupvar("output"),
+ "Shadowed Kernel methods were not handled correctly")
+ end
+ end
+
def test_autoloading_functions
assert_equal(false, Puppet::Parser::Functions.function(:autofunc),
"Got told autofunc already exists")
--
1.5.4.3
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"Puppet Developers" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at
http://groups.google.com/group/puppet-dev?hl=en
-~----------~----~----~----~------~----~------~--~---