Skip to content

Commit 2551911

Browse files
authored
Merge pull request #9414 from joshcooper/backport_deferred_sensitive_12050
[Backport 7.x] (PUP-12050) Check for nested Sensitive arguments
2 parents 46e88b4 + 20d1a37 commit 2551911

File tree

3 files changed

+117
-14
lines changed

3 files changed

+117
-14
lines changed

lib/puppet/pops/evaluator/deferred_resolver.rb

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,16 +88,25 @@ def resolve_futures(catalog)
8888
overrides = {}
8989
r.parameters.each_pair do |k, v|
9090
resolved = resolve(v)
91-
# If the value is instance of Sensitive - assign the unwrapped value
92-
# and mark it as sensitive if not already marked
93-
#
94-
if resolved.is_a?(Puppet::Pops::Types::PSensitiveType::Sensitive)
91+
case resolved
92+
when Puppet::Pops::Types::PSensitiveType::Sensitive
93+
# If the resolved value is instance of Sensitive - assign the unwrapped value
94+
# and mark it as sensitive if not already marked
95+
#
9596
resolved = resolved.unwrap
9697
mark_sensitive_parameters(r, k)
97-
# If the value is a DeferredValue and it has an argument of type PSensitiveType, mark it as sensitive
98-
# The DeferredValue.resolve method will unwrap it during catalog application
99-
elsif resolved.is_a?(Puppet::Pops::Evaluator::DeferredValue)
100-
if v.arguments.any? {|arg| arg.is_a?(Puppet::Pops::Types::PSensitiveType)}
98+
99+
when Puppet::Pops::Evaluator::DeferredValue
100+
# If the resolved value is a DeferredValue and it has an argument of type
101+
# PSensitiveType, mark it as sensitive. Since DeferredValues can nest,
102+
# we must walk all arguments, e.g. the DeferredValue may call the `epp`
103+
# function, where one of its arguments is a DeferredValue to call the
104+
# `vault:lookup` function.
105+
#
106+
# The DeferredValue.resolve method will unwrap the sensitive during
107+
# catalog application
108+
#
109+
if contains_sensitive_args?(v)
101110
mark_sensitive_parameters(r, k)
102111
end
103112
end
@@ -107,6 +116,33 @@ def resolve_futures(catalog)
107116
end
108117
end
109118

119+
# Return true if x contains an argument that is an instance of PSensitiveType:
120+
#
121+
# Deferred('new', [Sensitive, 'password'])
122+
#
123+
# Or an instance of PSensitiveType::Sensitive:
124+
#
125+
# Deferred('join', [['a', Sensitive('b')], ':'])
126+
#
127+
# Since deferred values can nest, descend into Arrays and Hash keys and values,
128+
# short-circuiting when the first occurrence is found.
129+
#
130+
def contains_sensitive_args?(x)
131+
case x
132+
when @deferred_class
133+
contains_sensitive_args?(x.arguments)
134+
when Array
135+
x.any? { |v| contains_sensitive_args?(v) }
136+
when Hash
137+
x.any? { |k, v| contains_sensitive_args?(k) || contains_sensitive_args?(v) }
138+
when Puppet::Pops::Types::PSensitiveType, Puppet::Pops::Types::PSensitiveType::Sensitive
139+
true
140+
else
141+
false
142+
end
143+
end
144+
private :contains_sensitive_args?
145+
110146
def mark_sensitive_parameters(r, k)
111147
unless r.sensitive_parameters.include?(k.to_sym)
112148
r.sensitive_parameters = (r.sensitive_parameters + [k.to_sym]).freeze

spec/integration/application/apply_spec.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -769,5 +769,20 @@ def bogus()
769769
}.to exit_with(0)
770770
.and output(/ensure: changed \[redacted\] to \[redacted\]/).to_stdout
771771
end
772+
773+
it "applies nested deferred sensitive file content" do
774+
manifest = <<~END
775+
$vars = {'token' => Deferred('new', [Sensitive, "hello"])}
776+
file { '#{deferred_file}':
777+
ensure => file,
778+
content => Deferred('inline_epp', ['<%= $token %>', $vars])
779+
}
780+
END
781+
apply.command_line.args = ['-e', manifest]
782+
expect {
783+
apply.run
784+
}.to exit_with(0)
785+
.and output(/ensure: changed \[redacted\] to \[redacted\]/).to_stdout
786+
end
772787
end
773788
end
Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,98 @@
11
require 'spec_helper'
22
require 'puppet_spec/compiler'
33

4+
Puppet::Type.newtype(:test_deferred) do
5+
newparam(:name)
6+
newproperty(:value)
7+
end
8+
49
describe Puppet::Pops::Evaluator::DeferredResolver do
510
include PuppetSpec::Compiler
611

712
let(:environment) { Puppet::Node::Environment.create(:testing, []) }
813
let(:facts) { Puppet::Node::Facts.new('node.example.com') }
914

15+
def compile_and_resolve_catalog(code, preprocess = false)
16+
catalog = compile_to_catalog(code)
17+
described_class.resolve_and_replace(facts, catalog, environment, preprocess)
18+
catalog
19+
end
20+
1021
it 'resolves deferred values in a catalog' do
11-
catalog = compile_to_catalog(<<~END)
22+
catalog = compile_and_resolve_catalog(<<~END, true)
1223
notify { "deferred":
1324
message => Deferred("join", [[1,2,3], ":"])
1425
}
1526
END
16-
described_class.resolve_and_replace(facts, catalog)
1727

1828
expect(catalog.resource(:notify, 'deferred')[:message]).to eq('1:2:3')
1929
end
2030

2131
it 'lazily resolves deferred values in a catalog' do
22-
catalog = compile_to_catalog(<<~END)
32+
catalog = compile_and_resolve_catalog(<<~END)
2333
notify { "deferred":
2434
message => Deferred("join", [[1,2,3], ":"])
2535
}
2636
END
27-
described_class.resolve_and_replace(facts, catalog, environment, false)
2837

2938
deferred = catalog.resource(:notify, 'deferred')[:message]
3039
expect(deferred.resolve).to eq('1:2:3')
3140
end
3241

3342
it 'lazily resolves nested deferred values in a catalog' do
34-
catalog = compile_to_catalog(<<~END)
43+
catalog = compile_and_resolve_catalog(<<~END)
3544
$args = Deferred("inline_epp", ["<%= 'a,b,c' %>"])
3645
notify { "deferred":
3746
message => Deferred("split", [$args, ","])
3847
}
3948
END
40-
described_class.resolve_and_replace(facts, catalog, environment, false)
4149

4250
deferred = catalog.resource(:notify, 'deferred')[:message]
4351
expect(deferred.resolve).to eq(["a", "b", "c"])
4452
end
4553

54+
it 'marks the parameter as sensitive when passed an array containing a Sensitive instance' do
55+
catalog = compile_and_resolve_catalog(<<~END)
56+
test_deferred { "deferred":
57+
value => Deferred('join', [['a', Sensitive('b')], ':'])
58+
}
59+
END
60+
61+
resource = catalog.resource(:test_deferred, 'deferred')
62+
expect(resource.sensitive_parameters).to eq([:value])
63+
end
64+
65+
it 'marks the parameter as sensitive when passed a hash containing a Sensitive key' do
66+
catalog = compile_and_resolve_catalog(<<~END)
67+
test_deferred { "deferred":
68+
value => Deferred('keys', [{Sensitive('key') => 'value'}])
69+
}
70+
END
71+
72+
resource = catalog.resource(:test_deferred, 'deferred')
73+
expect(resource.sensitive_parameters).to eq([:value])
74+
end
75+
76+
it 'marks the parameter as sensitive when passed a hash containing a Sensitive value' do
77+
catalog = compile_and_resolve_catalog(<<~END)
78+
test_deferred { "deferred":
79+
value => Deferred('values', [{key => Sensitive('value')}])
80+
}
81+
END
82+
83+
resource = catalog.resource(:test_deferred, 'deferred')
84+
expect(resource.sensitive_parameters).to eq([:value])
85+
end
86+
87+
it 'marks the parameter as sensitive when passed a nested Deferred containing a Sensitive type' do
88+
catalog = compile_and_resolve_catalog(<<~END)
89+
$vars = {'token' => Deferred('new', [Sensitive, "hello"])}
90+
test_deferred { "deferred":
91+
value => Deferred('inline_epp', ['<%= $token %>', $vars])
92+
}
93+
END
94+
95+
resource = catalog.resource(:test_deferred, 'deferred')
96+
expect(resource.sensitive_parameters).to eq([:value])
97+
end
4698
end

0 commit comments

Comments
 (0)