From 3baa2c1abc07a21acf9dd1430d4b97700c2835a1 Mon Sep 17 00:00:00 2001 From: Adam Leff Date: Tue, 9 Aug 2016 23:56:06 -0400 Subject: Allow flagging a resource property as sensitive Some properties in custom resources may include sensitive data, such as a password for a database server. When the Resource's state is built for use by Data Collector or similar auditing tool, `Chef::Resource#state_for_resource_reporter` builds a hash of all state properties for that resource and their values. This leads to sensitive data being transmitted and potentially stored in the clear. This change enhances properties with the ability to set an individual property as sensitive and then have the value of that property suppressed when exporting the Resource's state. --- lib/chef/mixin/properties.rb | 3 +++ lib/chef/property.rb | 13 ++++++++++++- lib/chef/resource.rb | 2 +- spec/unit/resource_spec.rb | 20 ++++++++++++++++++++ 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/lib/chef/mixin/properties.rb b/lib/chef/mixin/properties.rb index ae2406f1ae..8ff2cc4501 100644 --- a/lib/chef/mixin/properties.rb +++ b/lib/chef/mixin/properties.rb @@ -79,6 +79,9 @@ class Chef # part of desired state. Defaults to `true`. # @option options [Boolean] :identity `true` if this property # is part of object identity. Defaults to `false`. + # @option options [Boolean] :sensitive `true` if this property could + # contain sensitive information and whose value should be redacted + # in any resource reporting / auditing output. Defaults to `false`. # # @example Bare property # property :x diff --git a/lib/chef/property.rb b/lib/chef/property.rb index 3cb235b612..a357ba9ee3 100644 --- a/lib/chef/property.rb +++ b/lib/chef/property.rb @@ -229,6 +229,17 @@ class Chef options[:required] end + # + # Whether this property is sensitive or not. + # + # Defaults to false. + # + # @return [Boolean] + # + def sensitive? + options.fetch(:sensitive, false) + end + # # Validation options. (See Chef::Mixin::ParamsValidate#validate.) # @@ -236,7 +247,7 @@ class Chef # def validation_options @validation_options ||= options.reject do |k, v| - [:declared_in, :name, :instance_variable_name, :desired_state, :identity, :default, :name_property, :coerce, :required, :nillable].include?(k) + [:declared_in, :name, :instance_variable_name, :desired_state, :identity, :default, :name_property, :coerce, :required, :nillable, :sensitive].include?(k) end end diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb index 0de5c89475..d11fa1c80c 100644 --- a/lib/chef/resource.rb +++ b/lib/chef/resource.rb @@ -497,7 +497,7 @@ class Chef state_properties = self.class.state_properties state_properties.each do |property| if property.identity? || property.is_set?(self) - state[property.name] = send(property.name) + state[property.name] = property.sensitive? ? "*sensitive value suppressed*" : send(property.name) end end state diff --git a/spec/unit/resource_spec.rb b/spec/unit/resource_spec.rb index e35203c78a..68fc675b37 100644 --- a/spec/unit/resource_spec.rb +++ b/spec/unit/resource_spec.rb @@ -169,6 +169,26 @@ describe Chef::Resource do end end + describe "#state_for_resource_reporter" do + context "when a property is marked as sensitive" do + it "suppresses the sensitive property's value" do + resource_class = Class.new(Chef::Resource) { property :foo, String, sensitive: true } + resource = resource_class.new("sensitive_property_tests") + resource.foo = "some value" + expect(resource.state_for_resource_reporter[:foo]).to eq("*sensitive value suppressed*") + end + end + + context "when a property is not marked as sensitive" do + it "does not suppress the property's value" do + resource_class = Class.new(Chef::Resource) { property :foo, String } + resource = resource_class.new("sensitive_property_tests") + resource.foo = "some value" + expect(resource.state_for_resource_reporter[:foo]).to eq("some value") + end + end + end + describe "load_from" do let(:prior_resource) do prior_resource = Chef::Resource.new("funk") -- cgit v1.2.1