Skip to content

Commit f566bf1

Browse files
authored
Merge pull request #256 from puppetlabs/cat_2484_fix_yaml_overwrite
(CAT-2484) Prevent YAML corruption by making PuppetLint instances file-type aware
2 parents 6b988a2 + 04c7065 commit f566bf1

File tree

6 files changed

+115
-4
lines changed

6 files changed

+115
-4
lines changed

lib/puppet-lint.rb

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,42 @@ def print_problems
235235
report(@problems)
236236
end
237237

238+
# Public: Write fixes back to the file if this file type supports fixes.
239+
#
240+
# Returns nothing.
241+
def write_fixes
242+
return unless should_write_fixes?
243+
244+
File.binwrite(@path, @manifest)
245+
end
246+
247+
# Internal: Determine if fixes should be written for this file.
248+
#
249+
# Returns true if all conditions are met for writing fixes, false otherwise.
250+
def should_write_fixes?
251+
# Don't write if file type doesn't support fixes
252+
return false unless supports_fixes?
253+
254+
# Don't write if there are syntax errors (can't safely fix)
255+
return false if @problems&.any? { |r| r[:check] == :syntax }
256+
257+
# Don't write if there's no manifest content
258+
return false if @manifest.nil? || @manifest.empty?
259+
260+
true
261+
end
262+
263+
# Public: Determine if this file type supports automatic fixes.
264+
#
265+
# Returns true if fixes are supported for this file type, false otherwise.
266+
def supports_fixes?
267+
return false if @path.nil?
268+
269+
# Only .pp files support fixes currently
270+
# YAML files and other types may support fixes in the future
271+
File.extname(@path).match?(%r{\.pp$}i)
272+
end
273+
238274
# Public: Define a new check.
239275
#
240276
# name - A unique name for the check as a Symbol.

lib/puppet-lint/bin.rb

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,7 @@ def run
9090

9191
return_val = 1 if l.errors? || (l.warnings? && PuppetLint.configuration.fail_on_warnings)
9292

93-
next unless PuppetLint.configuration.fix && l.problems.none? { |r| r[:check] == :syntax }
94-
95-
File.binwrite(f, l.manifest)
93+
l.write_fixes if PuppetLint.configuration.fix
9694
end
9795

9896
if PuppetLint.configuration.sarif
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
class foo-bar {
2+
notify { 'hello': }
3+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
classes:
3+
foo-bar:
4+
parameters: []
5+
resources:
6+
notify:
7+
- title: hello

spec/unit/puppet-lint/bin_spec.rb

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -615,9 +615,27 @@ def initialize(args)
615615
end
616616
end
617617

618+
context 'when fixing a directory containing both .pp and .yaml files' do
619+
let(:args) { ['--fix', 'spec/fixtures/test/manifests/issue_254_overwriting_yaml'] }
620+
621+
its(:exitstatus) { is_expected.to eq(1) }
622+
623+
it 'does not overwrite YAML files' do
624+
yaml_file = 'spec/fixtures/test/manifests/issue_254_overwriting_yaml/class_with_dash.yaml'
625+
original_yaml = File.read(yaml_file)
626+
627+
bin # Run the command
628+
629+
yaml_after = File.read(yaml_file)
630+
expect(yaml_after).to eq(original_yaml)
631+
expect(yaml_after).to include('classes:')
632+
expect(yaml_after).not_to include('class foo-bar {')
633+
end
634+
end
635+
618636
context 'when overriding config file options with command line options' do
619637
context 'and config file sets "--only-checks=variable_contains_dash"' do
620-
around(:context) do |example|
638+
around(:each) do |example|
621639
Dir.mktmpdir do |tmpdir|
622640
Dir.chdir(tmpdir) do
623641
File.open('.puppet-lint.rc', 'wb') do |f|

spec/unit/puppet-lint/puppet-lint_spec.rb

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,53 @@
1515
linter.run
1616
expect(linter.manifest).to eq('')
1717
end
18+
19+
describe '#supports_fixes?' do
20+
context 'with a .pp file' do
21+
it 'returns true' do
22+
linter.instance_variable_set(:@path, 'test.pp')
23+
expect(linter.supports_fixes?).to be true
24+
end
25+
end
26+
27+
context 'with a .yaml file' do
28+
it 'returns false' do
29+
linter.instance_variable_set(:@path, 'test.yaml')
30+
expect(linter.supports_fixes?).to be false
31+
end
32+
end
33+
34+
context 'with no path set' do
35+
it 'returns false' do
36+
expect(linter.supports_fixes?).to be false
37+
end
38+
end
39+
end
40+
41+
describe '#should_write_fixes?' do
42+
before(:each) do
43+
linter.instance_variable_set(:@path, 'test.pp')
44+
linter.instance_variable_set(:@manifest, 'class test { }')
45+
end
46+
47+
context 'when file supports fixes and has no syntax errors' do
48+
it 'returns true' do
49+
expect(linter.should_write_fixes?).to be true
50+
end
51+
end
52+
53+
context 'when file has syntax errors' do
54+
it 'returns false' do
55+
linter.instance_variable_set(:@problems, [{ check: :syntax }])
56+
expect(linter.should_write_fixes?).to be false
57+
end
58+
end
59+
60+
context 'when file type does not support fixes' do
61+
it 'returns false' do
62+
linter.instance_variable_set(:@path, 'test.yaml')
63+
expect(linter.should_write_fixes?).to be false
64+
end
65+
end
66+
end
1867
end

0 commit comments

Comments
 (0)