puppet 4.9.4 → 4.10.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puppet might be problematic. Click here for more details.

Files changed (124) hide show
  1. checksums.yaml +7 -0
  2. data/Rakefile +6 -0
  3. data/ext/project_data.yaml +2 -2
  4. data/lib/hiera/puppet_function.rb +1 -1
  5. data/lib/puppet.rb +1 -0
  6. data/lib/puppet/application.rb +14 -0
  7. data/lib/puppet/application/inspect.rb +3 -0
  8. data/lib/puppet/defaults.rb +12 -2
  9. data/lib/puppet/etc.rb +125 -0
  10. data/lib/puppet/face/help.rb +1 -1
  11. data/lib/puppet/functions.rb +49 -4
  12. data/lib/puppet/functions/eyaml_lookup_key.rb +12 -3
  13. data/lib/puppet/functions/hocon_data.rb +9 -0
  14. data/lib/puppet/functions/json_data.rb +9 -0
  15. data/lib/puppet/functions/yaml_data.rb +9 -0
  16. data/lib/puppet/indirector/file_bucket_file/file.rb +69 -22
  17. data/lib/puppet/indirector/key/file.rb +2 -1
  18. data/lib/puppet/indirector/ssl_file.rb +24 -3
  19. data/lib/puppet/module.rb +28 -22
  20. data/lib/puppet/network/http/compression.rb +2 -1
  21. data/lib/puppet/parser/compiler.rb +15 -38
  22. data/lib/puppet/parser/functions/hiera.rb +1 -1
  23. data/lib/puppet/parser/functions/hiera_array.rb +1 -1
  24. data/lib/puppet/parser/functions/hiera_hash.rb +1 -1
  25. data/lib/puppet/parser/functions/hiera_include.rb +1 -1
  26. data/lib/puppet/parser/scope.rb +59 -17
  27. data/lib/puppet/pops/evaluator/callable_signature.rb +7 -0
  28. data/lib/puppet/pops/functions/dispatch.rb +18 -5
  29. data/lib/puppet/pops/functions/dispatcher.rb +7 -13
  30. data/lib/puppet/pops/issue_reporter.rb +1 -1
  31. data/lib/puppet/pops/issues.rb +84 -0
  32. data/lib/puppet/pops/loader/base_loader.rb +13 -5
  33. data/lib/puppet/pops/lookup/configured_data_provider.rb +8 -2
  34. data/lib/puppet/pops/lookup/data_dig_function_provider.rb +109 -19
  35. data/lib/puppet/pops/lookup/data_hash_function_provider.rb +19 -4
  36. data/lib/puppet/pops/lookup/data_provider.rb +43 -29
  37. data/lib/puppet/pops/lookup/environment_data_provider.rb +1 -1
  38. data/lib/puppet/pops/lookup/explainer.rb +1 -0
  39. data/lib/puppet/pops/lookup/function_provider.rb +36 -11
  40. data/lib/puppet/pops/lookup/global_data_provider.rb +18 -5
  41. data/lib/puppet/pops/lookup/hiera_config.rb +203 -84
  42. data/lib/puppet/pops/lookup/interpolation.rb +21 -6
  43. data/lib/puppet/pops/lookup/invocation.rb +14 -9
  44. data/lib/puppet/pops/lookup/location_resolver.rb +27 -0
  45. data/lib/puppet/pops/lookup/lookup_adapter.rb +59 -6
  46. data/lib/puppet/pops/lookup/lookup_key_function_provider.rb +9 -77
  47. data/lib/puppet/pops/lookup/module_data_provider.rb +27 -4
  48. data/lib/puppet/pops/parser/lexer2.rb +1 -1
  49. data/lib/puppet/pops/pcore.rb +3 -3
  50. data/lib/puppet/pops/types/p_object_type.rb +4 -6
  51. data/lib/puppet/pops/types/ruby_generator.rb +2 -2
  52. data/lib/puppet/pops/types/type_asserter.rb +3 -3
  53. data/lib/puppet/pops/types/type_mismatch_describer.rb +25 -7
  54. data/lib/puppet/pops/types/types.rb +20 -29
  55. data/lib/puppet/provider/exec.rb +4 -2
  56. data/lib/puppet/provider/nameservice.rb +8 -8
  57. data/lib/puppet/provider/selmodule/semodule.rb +20 -16
  58. data/lib/puppet/provider/service/src.rb +39 -39
  59. data/lib/puppet/provider/service/systemd.rb +1 -1
  60. data/lib/puppet/provider/user/aix.rb +7 -2
  61. data/lib/puppet/settings.rb +30 -17
  62. data/lib/puppet/ssl/base.rb +14 -1
  63. data/lib/puppet/ssl/certificate_authority.rb +4 -2
  64. data/lib/puppet/ssl/configuration.rb +4 -1
  65. data/lib/puppet/ssl/inventory.rb +10 -3
  66. data/lib/puppet/ssl/key.rb +7 -3
  67. data/lib/puppet/test/test_helper.rb +3 -0
  68. data/lib/puppet/type.rb +13 -1
  69. data/lib/puppet/type/exec.rb +16 -1
  70. data/lib/puppet/type/group.rb +17 -11
  71. data/lib/puppet/type/user.rb +3 -1
  72. data/lib/puppet/util.rb +1 -0
  73. data/lib/puppet/util/character_encoding.rb +95 -0
  74. data/lib/puppet/util/execution.rb +9 -6
  75. data/lib/puppet/util/reference.rb +4 -2
  76. data/lib/puppet/util/windows/file.rb +5 -1
  77. data/lib/puppet/version.rb +6 -2
  78. data/locales/config.yaml +1 -1
  79. data/locales/puppet.pot +18 -4
  80. data/spec/integration/ssl/autosign_spec.rb +18 -3
  81. data/spec/integration/ssl/key_spec.rb +104 -0
  82. data/spec/integration/type/user_spec.rb +13 -6
  83. data/spec/spec_helper.rb +7 -0
  84. data/spec/unit/application/inspect_spec.rb +9 -2
  85. data/spec/unit/data_providers/function_data_provider_spec.rb +2 -2
  86. data/spec/unit/etc_spec.rb +234 -0
  87. data/spec/unit/face/certificate_spec.rb +10 -2
  88. data/spec/unit/functions/dig_spec.rb +1 -1
  89. data/spec/unit/functions/hiera_spec.rb +40 -1
  90. data/spec/unit/functions/lookup_fixture_spec.rb +10 -10
  91. data/spec/unit/functions/lookup_spec.rb +1217 -357
  92. data/spec/unit/functions4_spec.rb +37 -1
  93. data/spec/unit/indirector/file_bucket_file/file_spec.rb +33 -2
  94. data/spec/unit/indirector/key/file_spec.rb +1 -1
  95. data/spec/unit/indirector/ssl_file_spec.rb +3 -3
  96. data/spec/unit/module_spec.rb +52 -59
  97. data/spec/unit/network/http/compression_spec.rb +39 -8
  98. data/spec/unit/parser/compiler_spec.rb +14 -0
  99. data/spec/unit/pops/loaders/loaders_spec.rb +21 -3
  100. data/spec/unit/pops/loaders/module_loaders_spec.rb +61 -0
  101. data/spec/unit/pops/lookup/context_spec.rb +56 -8
  102. data/spec/unit/pops/lookup/lookup_spec.rb +32 -1
  103. data/spec/unit/pops/parser/lexer2_spec.rb +8 -0
  104. data/spec/unit/pops/types/ruby_generator_spec.rb +48 -0
  105. data/spec/unit/pops/types/type_mismatch_describer_spec.rb +12 -3
  106. data/spec/unit/pops/types/types_spec.rb +6 -7
  107. data/spec/unit/provider/nameservice_spec.rb +12 -12
  108. data/spec/unit/provider/package/pkg_spec.rb +2 -0
  109. data/spec/unit/provider/service/src_spec.rb +5 -0
  110. data/spec/unit/ssl/base_spec.rb +9 -0
  111. data/spec/unit/ssl/certificate_authority_spec.rb +2 -2
  112. data/spec/unit/ssl/certificate_request_attributes_spec.rb +6 -0
  113. data/spec/unit/ssl/certificate_request_spec.rb +1 -1
  114. data/spec/unit/ssl/certificate_spec.rb +1 -1
  115. data/spec/unit/ssl/configuration_spec.rb +11 -2
  116. data/spec/unit/ssl/inventory_spec.rb +27 -3
  117. data/spec/unit/ssl/key_spec.rb +7 -7
  118. data/spec/unit/type/exec_spec.rb +41 -4
  119. data/spec/unit/type/file_spec.rb +4 -1
  120. data/spec/unit/util/character_encoding_spec.rb +88 -0
  121. data/spec/unit/util/execution_spec.rb +12 -0
  122. data/spec/unit/version_spec.rb +4 -0
  123. metadata +3803 -3808
  124. data/tasks/i18n.rake +0 -20
@@ -9,6 +9,8 @@ describe Puppet::Type.type(:package).provider(:pkg) do
9
9
  # Get a pid for $CHILD_STATUS to latch on to
10
10
  command = "cmd.exe /c \"exit 0\""
11
11
  Puppet::Util::Execution.execute(command, {:failonfail => false})
12
+ else
13
+ Puppet::Util::Execution.execute('exit 0', {:failonfail => false})
12
14
  end
13
15
 
14
16
  before :each do
@@ -167,6 +167,11 @@ _EOF_
167
167
  @provider.expects(:execute).with(['/usr/bin/lssrc', '-s', "myservice"]).returns sample_output
168
168
  expect(@provider.status).to eq(nil)
169
169
  end
170
+
171
+ it "should consider a non-existing service to be have a status of :stopped" do
172
+ @provider.expects(:execute).with(['/usr/bin/lssrc', '-s', 'myservice']).raises(Puppet::ExecutionFailure, "fail")
173
+ expect(@provider.status).to eq(:stopped)
174
+ end
170
175
  end
171
176
 
172
177
  describe "when restarting a service" do
@@ -45,6 +45,15 @@ describe Puppet::SSL::Certificate do
45
45
  end
46
46
  end
47
47
 
48
+ describe "when initializing wrapped class from a file with #read" do
49
+ it "should open the file with ASCII encoding" do
50
+ path = '/foo/bar/cert'
51
+ Puppet::SSL::Base.stubs(:valid_certname).returns(true)
52
+ Puppet::FileSystem.expects(:read).with(path, :encoding => Encoding::ASCII).returns("bar")
53
+ @base.read(path)
54
+ end
55
+ end
56
+
48
57
  describe "#digest_algorithm" do
49
58
  let(:content) { stub 'content' }
50
59
  let(:base) {
@@ -167,7 +167,7 @@ describe Puppet::SSL::CertificateAuthority do
167
167
  Puppet::FileSystem.expects(:exist?).with(Puppet[:capass]).returns false
168
168
 
169
169
  fh = StringIO.new
170
- Puppet.settings.setting(:capass).expects(:open).with('w').yields fh
170
+ Puppet.settings.setting(:capass).expects(:open).with('w:ASCII').yields fh
171
171
 
172
172
  @ca.stubs(:sign)
173
173
 
@@ -1067,7 +1067,7 @@ describe "CertificateAuthority.generate" do
1067
1067
  end
1068
1068
 
1069
1069
  def expect_to_write_the_ca_password
1070
- Puppet.settings.setting(:capass).expects(:open).with('w')
1070
+ Puppet.settings.setting(:capass).expects(:open).with('w:ASCII')
1071
1071
  end
1072
1072
 
1073
1073
  def expect_ca_initialization
@@ -9,6 +9,12 @@ describe Puppet::SSL::CertificateRequestAttributes do
9
9
  "custom_attributes" => {
10
10
  "1.3.6.1.4.1.34380.2.2"=>[3232235521, 3232235777], # system IPs in hex
11
11
  "1.3.6.1.4.1.34380.2.0"=>"hostname.domain.com",
12
+ # different UTF-8 widths
13
+ # 1-byte A
14
+ # 2-byte ۿ - http://www.fileformat.info/info/unicode/char/06ff/index.htm - 0xDB 0xBF / 219 191
15
+ # 3-byte ᚠ - http://www.fileformat.info/info/unicode/char/16A0/index.htm - 0xE1 0x9A 0xA0 / 225 154 160
16
+ # 4-byte 𠜎 - http://www.fileformat.info/info/unicode/char/2070E/index.htm - 0xF0 0xA0 0x9C 0x8E / 240 160 156 142
17
+ "1.2.840.113549.1.9.7"=>"utf8passwordA\u06FF\u16A0\u{2070E}"
12
18
  }
13
19
  }
14
20
  end
@@ -60,7 +60,7 @@ describe Puppet::SSL::CertificateRequest do
60
60
 
61
61
  it "should be able to read requests from disk" do
62
62
  path = "/my/path"
63
- File.expects(:read).with(path).returns("my request")
63
+ Puppet::FileSystem.expects(:read).with(path, :encoding => Encoding::ASCII).returns("my request")
64
64
  my_req = mock 'request'
65
65
  OpenSSL::X509::Request.expects(:new).with("my request").returns(my_req)
66
66
  expect(request.read(path)).to equal(my_req)
@@ -162,7 +162,7 @@ describe Puppet::SSL::Certificate do
162
162
 
163
163
  it "should be able to read certificates from disk" do
164
164
  path = "/my/path"
165
- File.expects(:read).with(path).returns("my certificate")
165
+ Puppet::FileSystem.expects(:read).with(path, :encoding => Encoding::ASCII).returns("my certificate")
166
166
  certificate = mock 'certificate'
167
167
  OpenSSL::X509::Certificate.expects(:new).with("my certificate").returns(certificate)
168
168
  expect(@certificate.read(path)).to equal(certificate)
@@ -9,6 +9,13 @@ describe Puppet::SSL::Configuration do
9
9
 
10
10
  let(:ssl_server_ca_auth) { "/path/to/certs/ssl_server_ca_auth.pem" }
11
11
 
12
+ # different UTF-8 widths
13
+ # 1-byte A
14
+ # 2-byte ۿ - http://www.fileformat.info/info/unicode/char/06ff/index.htm - 0xDB 0xBF / 219 191
15
+ # 3-byte ᚠ - http://www.fileformat.info/info/unicode/char/16A0/index.htm - 0xE1 0x9A 0xA0 / 225 154 160
16
+ # 4-byte 𠜎 - http://www.fileformat.info/info/unicode/char/2070E/index.htm - 0xF0 0xA0 0x9C 0x8E / 240 160 156 142
17
+ let (:mixed_utf8) { "A\u06FF\u16A0\u{2070E}" } # Aۿᚠ𠜎
18
+
12
19
  it "should require the localcacert argument" do
13
20
  expect { subject }.to raise_error ArgumentError
14
21
  end
@@ -44,8 +51,7 @@ describe Puppet::SSL::Configuration do
44
51
  end
45
52
 
46
53
  it "#ca_auth_certificates returns an Array<OpenSSL::X509::Certificate>" do
47
- subject.stubs(:read_file).returns(master_ca_pem + root_ca_pem)
48
-
54
+ Puppet::FileSystem.expects(:read).with(subject.ca_auth_file, :encoding => Encoding::UTF_8).returns(master_ca_pem + root_ca_pem)
49
55
  certs = subject.ca_auth_certificates
50
56
  certs.each { |cert| expect(cert).to be_a_kind_of OpenSSL::X509::Certificate }
51
57
  end
@@ -67,6 +73,7 @@ describe Puppet::SSL::Configuration do
67
73
  # certificates. It is signed by the Root CA.
68
74
  def master_ca_pem
69
75
  @master_ca_pem ||= <<-AUTH_BUNDLE
76
+ # Master CA #{mixed_utf8}
70
77
  -----BEGIN CERTIFICATE-----
71
78
  MIICljCCAf+gAwIBAgIBAjANBgkqhkiG9w0BAQUFADBJMRAwDgYDVQQDDAdSb290
72
79
  IENBMRowGAYDVQQLDBFTZXJ2ZXIgT3BlcmF0aW9uczEZMBcGA1UECgwQRXhhbXBs
@@ -89,6 +96,7 @@ l1k75xLLIcrlsDYo3999KOSSchH2K7bLT7TuQ2okdP6FHWmeWmudewlu
89
96
  # This is the Root CA
90
97
  def root_ca_pem
91
98
  @root_ca_pem ||= <<-LOCALCACERT
99
+ # Root CA #{mixed_utf8}
92
100
  -----BEGIN CERTIFICATE-----
93
101
  MIICYDCCAcmgAwIBAgIJALf2Pk2HvtBzMA0GCSqGSIb3DQEBBQUAMEkxEDAOBgNV
94
102
  BAMMB1Jvb3QgQ0ExGjAYBgNVBAsMEVNlcnZlciBPcGVyYXRpb25zMRkwFwYDVQQK
@@ -111,6 +119,7 @@ rPsNU/Tx19jHC87oXlmAePLI4IaUHXrWb7CRbY9TEcPdmj1R
111
119
  # signed by the Root CA.
112
120
  def agent_ca_pem
113
121
  @agent_ca_pem ||= <<-AGENT_CA
122
+ # Agent CA #{mixed_utf8}
114
123
  -----BEGIN CERTIFICATE-----
115
124
  MIIClTCCAf6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBJMRAwDgYDVQQDDAdSb290
116
125
  IENBMRowGAYDVQQLDBFTZXJ2ZXIgT3BlcmF0aW9uczEZMBcGA1UECgwQRXhhbXBs
@@ -5,6 +5,14 @@ require 'puppet/ssl/inventory'
5
5
 
6
6
  describe Puppet::SSL::Inventory, :unless => Puppet.features.microsoft_windows? do
7
7
  let(:cert_inventory) { File.expand_path("/inven/tory") }
8
+
9
+ # different UTF-8 widths
10
+ # 1-byte A
11
+ # 2-byte ۿ - http://www.fileformat.info/info/unicode/char/06ff/index.htm - 0xDB 0xBF / 219 191
12
+ # 3-byte ᚠ - http://www.fileformat.info/info/unicode/char/16A0/index.htm - 0xE1 0x9A 0xA0 / 225 154 160
13
+ # 4-byte ܎ - http://www.fileformat.info/info/unicode/char/2070E/index.htm - 0xF0 0xA0 0x9C 0x8E / 240 160 156 142
14
+ let (:mixed_utf8) { "A\u06FF\u16A0\u{2070E}" } # Aۿᚠ܎
15
+
8
16
  before do
9
17
  @class = Puppet::SSL::Inventory
10
18
  end
@@ -56,7 +64,7 @@ describe Puppet::SSL::Inventory, :unless => Puppet.features.microsoft_windows? d
56
64
  describe "and adding a certificate" do
57
65
 
58
66
  it "should use the Settings to write to the file" do
59
- Puppet.settings.setting(:cert_inventory).expects(:open).with("a")
67
+ Puppet.settings.setting(:cert_inventory).expects(:open).with('a:UTF-8')
60
68
 
61
69
  @inventory.add(@cert)
62
70
  end
@@ -66,7 +74,7 @@ describe Puppet::SSL::Inventory, :unless => Puppet.features.microsoft_windows? d
66
74
  cert.content = @cert
67
75
 
68
76
  fh = StringIO.new
69
- Puppet.settings.setting(:cert_inventory).expects(:open).with("a").yields(fh)
77
+ Puppet.settings.setting(:cert_inventory).expects(:open).with('a:UTF-8').yields(fh)
70
78
 
71
79
  @inventory.expects(:format).with(@cert).returns "myformat"
72
80
 
@@ -74,6 +82,22 @@ describe Puppet::SSL::Inventory, :unless => Puppet.features.microsoft_windows? d
74
82
 
75
83
  expect(fh.string).to eq("myformat")
76
84
  end
85
+
86
+ it "can use UTF-8 in the CN per RFC 5280" do
87
+ cert = Puppet::SSL::Certificate.new("mycert")
88
+ cert.content = stub 'cert',
89
+ :serial => 1,
90
+ :not_before => Time.now,
91
+ :not_after => Time.now,
92
+ :subject => "/CN=#{mixed_utf8}"
93
+
94
+ fh = StringIO.new
95
+ Puppet.settings.setting(:cert_inventory).expects(:open).with('a:UTF-8').yields(fh)
96
+
97
+ @inventory.add(cert)
98
+
99
+ expect(fh.string).to match(/\/CN=#{mixed_utf8}/)
100
+ end
77
101
  end
78
102
 
79
103
  describe "and formatting a certificate" do
@@ -118,7 +142,7 @@ describe Puppet::SSL::Inventory, :unless => Puppet.features.microsoft_windows? d
118
142
  end
119
143
 
120
144
  it "should return the all the serial numbers from the lines matching the provided name" do
121
- File.expects(:readlines).with(cert_inventory).returns ["0x00f blah blah /CN=me\n", "0x001 blah blah /CN=you\n", "0x002 blah blah /CN=me\n"]
145
+ File.expects(:readlines).with(cert_inventory, :encoding => Encoding::UTF_8).returns ["0x00f blah blah /CN=me\n", "0x001 blah blah /CN=you\n", "0x002 blah blah /CN=me\n"]
122
146
 
123
147
  expect(@inventory.serials("me")).to eq([15, 2])
124
148
  end
@@ -63,7 +63,7 @@ describe Puppet::SSL::Key do
63
63
 
64
64
  it "should be able to read keys from disk" do
65
65
  path = "/my/path"
66
- File.expects(:read).with(path).returns("my key")
66
+ Puppet::FileSystem.expects(:read).with(path, :encoding => Encoding::ASCII).returns("my key")
67
67
  key = mock 'key'
68
68
  OpenSSL::PKey::RSA.expects(:new).returns(key)
69
69
  expect(@key.read(path)).to equal(key)
@@ -76,9 +76,9 @@ describe Puppet::SSL::Key do
76
76
 
77
77
  path = "/my/path"
78
78
 
79
- File.stubs(:read).with(path).returns("my key")
79
+ Puppet::FileSystem.stubs(:read).with(path, :encoding => Encoding::ASCII).returns("my key")
80
80
  OpenSSL::PKey::RSA.expects(:new).with("my key", nil).returns(mock('key'))
81
- File.expects(:read).with("/path/to/password").never
81
+ Puppet::FileSystem.expects(:read).with("/path/to/password", :encoding => Encoding::BINARY).never
82
82
 
83
83
  @key.read(path)
84
84
  end
@@ -88,8 +88,8 @@ describe Puppet::SSL::Key do
88
88
  @key.password_file = "/path/to/password"
89
89
 
90
90
  path = "/my/path"
91
- File.expects(:read).with(path).returns("my key")
92
- File.expects(:read).with("/path/to/password").returns("my password")
91
+ Puppet::FileSystem.expects(:read).with(path, :encoding => Encoding::ASCII).returns("my key")
92
+ Puppet::FileSystem.expects(:read).with("/path/to/password", :encoding => Encoding::BINARY).returns("my password")
93
93
 
94
94
  key = mock 'key'
95
95
  OpenSSL::PKey::RSA.expects(:new).with("my key", "my password").returns(key)
@@ -155,7 +155,7 @@ describe Puppet::SSL::Key do
155
155
  describe "with a password file set" do
156
156
  it "should return a nil password if the password file does not exist" do
157
157
  Puppet::FileSystem.expects(:exist?).with("/path/to/pass").returns false
158
- File.expects(:read).with("/path/to/pass").never
158
+ Puppet::FileSystem.expects(:read).with("/path/to/pass", :encoding => Encoding::BINARY).never
159
159
 
160
160
  @instance.password_file = "/path/to/pass"
161
161
 
@@ -164,7 +164,7 @@ describe Puppet::SSL::Key do
164
164
 
165
165
  it "should return the contents of the password file as its password" do
166
166
  Puppet::FileSystem.expects(:exist?).with("/path/to/pass").returns true
167
- File.expects(:read).with("/path/to/pass").returns "my password"
167
+ Puppet::FileSystem.expects(:read).with("/path/to/pass", :encoding => Encoding::BINARY).returns "my password"
168
168
 
169
169
  @instance.password_file = "/path/to/pass"
170
170
 
@@ -67,7 +67,12 @@ describe Puppet::Type.type(:exec) do
67
67
 
68
68
  it "should report a failure" do
69
69
  expect { exec_tester('false', 1).refresh }.
70
- to raise_error(Puppet::Error, /^false returned 1 instead of/)
70
+ to raise_error(Puppet::Error, /^'false' returned 1 instead of/)
71
+ end
72
+
73
+ it "should redact sensitive commands on failure" do
74
+ expect { exec_tester('false', 1, :sensitive_parameters => [:command]).refresh }.
75
+ to raise_error(Puppet::Error, /^\[command redacted\] returned 1 instead of/)
71
76
  end
72
77
 
73
78
  it "should not report a failure if the exit status is specified in a returns array" do
@@ -76,7 +81,12 @@ describe Puppet::Type.type(:exec) do
76
81
 
77
82
  it "should report a failure if the exit status is not specified in a returns array" do
78
83
  expect { exec_tester('false', 1, :returns => [0, 100]).refresh }.
79
- to raise_error(Puppet::Error, /^false returned 1 instead of/)
84
+ to raise_error(Puppet::Error, /^'false' returned 1 instead of/)
85
+ end
86
+
87
+ it "should report redact sensitive commands if the exit status is not specified in a returns array" do
88
+ expect { exec_tester('false', 1, :returns => [0, 100], :sensitive_parameters => [:command]).refresh }.
89
+ to raise_error(Puppet::Error, /^\[command redacted\] returned 1 instead of/)
80
90
  end
81
91
 
82
92
  it "should log the output on success" do
@@ -106,7 +116,19 @@ describe Puppet::Type.type(:exec) do
106
116
  it "should log the output on failure" do
107
117
  output = "output1\noutput2\n"
108
118
  expect { exec_tester('false', 1, :output => output, :logoutput => :on_failure).refresh }.
109
- to raise_error(Puppet::Error, /^false returned 1 instead of/)
119
+ to raise_error(Puppet::Error, /^'false' returned 1 instead of/)
120
+
121
+ output.split("\n").each do |line|
122
+ log = @logs.shift
123
+ expect(log.level).to eq(:err)
124
+ expect(log.message).to eq(line)
125
+ end
126
+ end
127
+
128
+ it "should redact the command on failure" do
129
+ output = "output1\noutput2\n"
130
+ expect { exec_tester('false', 1, :output => output, :logoutput => :on_failure, :sensitive_parameters => [:command]).refresh }.
131
+ to raise_error(Puppet::Error, /^\[command redacted\] returned 1 instead of/)
110
132
 
111
133
  output.split("\n").each do |line|
112
134
  log = @logs.shift
@@ -121,7 +143,22 @@ describe Puppet::Type.type(:exec) do
121
143
  expect {
122
144
  exec_tester('false', 1, :output => output, :returns => [0, 100],
123
145
  :logoutput => :on_failure).refresh
124
- }.to raise_error(Puppet::Error, /^false returned 1 instead of/)
146
+ }.to raise_error(Puppet::Error, /^'false' returned 1 instead of/)
147
+
148
+ output.split("\n").each do |line|
149
+ log = @logs.shift
150
+ expect(log.level).to eq(:err)
151
+ expect(log.message).to eq(line)
152
+ end
153
+ end
154
+
155
+ it "should redact the command on failure when returns is specified as an array" do
156
+ output = "output1\noutput2\n"
157
+
158
+ expect {
159
+ exec_tester('false', 1, :output => output, :returns => [0, 100],
160
+ :logoutput => :on_failure, :sensitive_parameters => [:command]).refresh
161
+ }.to raise_error(Puppet::Error, /^\[command redacted\] returned 1 instead of/)
125
162
 
126
163
  output.split("\n").each do |line|
127
164
  log = @logs.shift
@@ -1598,7 +1598,10 @@ describe Puppet::Type.type(:file) do
1598
1598
  catalog.apply
1599
1599
 
1600
1600
  expect(Puppet::FileSystem.exist?(path)).to be_truthy
1601
- expect(@logs).not_to be_any {|l| l.level != :notice }
1601
+ expect(@logs).not_to be_any { |l|
1602
+ # the audit metaparameter is deprecated and logs a warning
1603
+ l.level != :notice
1604
+ }
1602
1605
  end
1603
1606
  end
1604
1607
 
@@ -0,0 +1,88 @@
1
+ #! /usr/bin/env ruby
2
+ require 'spec_helper'
3
+ require 'puppet/util/character_encoding'
4
+
5
+ describe Puppet::Util::CharacterEncoding do
6
+ describe "::convert_to_utf_8!" do
7
+ context "when passed a string that is already UTF-8" do
8
+ context "with valid encoding" do
9
+ it "should return the string unaltered" do
10
+ utf8_string = "\u06FF\u2603"
11
+ expect(Puppet::Util::CharacterEncoding.convert_to_utf_8!(utf8_string)).to eq(utf8_string)
12
+ end
13
+ end
14
+
15
+ context "with invalid encoding" do
16
+ let(:invalid_utf8_string) { "\xfd\xf1".force_encoding(Encoding::UTF_8) }
17
+
18
+ it "should issue a debug message" do
19
+ Puppet.expects(:debug).with(regexp_matches(/not valid UTF-8/))
20
+ Puppet::Util::CharacterEncoding.convert_to_utf_8!(invalid_utf8_string)
21
+ end
22
+
23
+ it "should return nil" do
24
+ expect(Puppet::Util::CharacterEncoding.convert_to_utf_8!(invalid_utf8_string)).to be_nil
25
+ end
26
+ end
27
+ end
28
+
29
+ context "when passed a string not in UTF-8 encoding" do
30
+ context "the bytes of which represent valid UTF-8" do
31
+ # I think this effectively what the ruby Etc module is doing when it
32
+ # returns strings read in from /etc/passwd and /etc/group
33
+ let(:iso_8859_1_string) { [225, 154, 160].pack('C*').force_encoding(Encoding::ISO_8859_1) }
34
+ let(:result) { Puppet::Util::CharacterEncoding.convert_to_utf_8!(iso_8859_1_string) }
35
+
36
+ it "should set external encoding to UTF-8" do
37
+ expect(result.encoding).to eq(Encoding::UTF_8)
38
+ end
39
+
40
+ it "should not modify the bytes (transcode) the string" do
41
+ expect(result.bytes.to_a).to eq([225, 154, 160])
42
+ end
43
+ end
44
+
45
+ context "the bytes of which do not represent valid UTF-8" do
46
+ it "should transcode the string to UTF-8 if it is transcodable" do
47
+ # http://www.fileformat.info/info/unicode/char/3050/index.htm
48
+ # ぐ - HIRAGANA LETTER GU
49
+ # In Shift_JIS: \x82 \xae - 130 174
50
+ # In Unicode: \u3050 - \xe3 \x81 \x90 - 227 129 144
51
+ # if we were only ruby > 2.3.0, we could do String.new("\x82\xae", :encoding => Encoding::Shift_JIS)
52
+ as_shift_jis = [130, 174].pack('C*').force_encoding(Encoding::Shift_JIS)
53
+ as_utf8 = "\u3050"
54
+
55
+ # this is not valid UTF-8
56
+ expect(as_shift_jis.dup.force_encoding(Encoding::UTF_8).valid_encoding?).to be_falsey
57
+
58
+ result = Puppet::Util::CharacterEncoding.convert_to_utf_8!(as_shift_jis)
59
+ expect(result).to eq(as_utf8)
60
+ # largely redundant but reinforces the point - this was transcoded:
61
+ expect(result.bytes.to_a).to eq([227, 129, 144])
62
+ end
63
+
64
+ context "if it is not transcodable" do
65
+ let(:as_ascii) { [254, 241].pack('C*').force_encoding(Encoding::ASCII) }
66
+ it "should issue a debug message and return nil if not transcodable" do
67
+ # An admittedly contrived case, but perhaps not so improbable
68
+ # http://www.fileformat.info/info/unicode/char/5e0c/index.htm
69
+ # 希 Han Character 'rare; hope, expect, strive for'
70
+ # In EUC_KR: \xfd \xf1 - 253 241
71
+ # In Unicode: \u5e0c - \xe5 \xb8 \x8c - 229 184 140
72
+
73
+ # If the original system value is in EUC_KR, and puppet (ruby) is run
74
+ # in ISO_8859_1, this value will be read in as ASCII, with invalid
75
+ # escape sequences in that encoding. It is also not valid unicode
76
+ # as-is. This scenario is one we can't recover from, so fail.
77
+ Puppet.expects(:debug).with(regexp_matches(/not valid UTF-8/))
78
+ Puppet::Util::CharacterEncoding.convert_to_utf_8!(as_ascii)
79
+ end
80
+
81
+ it "should return nil" do
82
+ expect(Puppet::Util::CharacterEncoding.convert_to_utf_8!(as_ascii)).to be_nil
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -512,6 +512,10 @@ describe Puppet::Util::Execution do
512
512
  Puppet::Util::Execution.expects(:debug).with("Executing with uid=100 gid=mygroup: 'echo hello'")
513
513
  Puppet::Util::Execution.execute('echo hello', {:uid => 100, :gid => 'mygroup'})
514
514
  end
515
+ it 'should redact commands in debug output when passed sensitive option' do
516
+ Puppet::Util::Execution.expects(:debug).with("Executing: '[redacted]'")
517
+ Puppet::Util::Execution.execute('echo hello', {:sensitive => true})
518
+ end
515
519
  end
516
520
 
517
521
  describe "after execution" do
@@ -587,6 +591,14 @@ describe Puppet::Util::Execution do
587
591
  }.to raise_error(Puppet::ExecutionFailure, /Execution of 'fail command' returned 1/)
588
592
  end
589
593
 
594
+ it "should raise an error with redacted sensitive command if failonfail is true and the child failed" do
595
+ stub_process_wait(1)
596
+
597
+ expect {
598
+ subject.execute('fail command', :failonfail => true, :sensitive => true)
599
+ }.to raise_error(Puppet::ExecutionFailure, /Execution of '\[redacted\]' returned 1/)
600
+ end
601
+
590
602
  it "should not raise an error if failonfail is false and the child failed" do
591
603
  stub_process_wait(1)
592
604