This repository has been archived by the owner on Sep 19, 2020. It is now read-only.
/
rules.rb
110 lines (102 loc) · 4.92 KB
/
rules.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
rule "FC002", "Avoid string interpolation where not required" do
tags %w{style strings}
description "When setting a resource value avoid string interpolation where not required."
recipe do |ast|
ast.xpath(%q{//string_literal[count(descendant::string_embexpr) = 1 and
count(string_add/tstring_content|string_add/string_add/tstring_content) = 0]}).map{|str| match(str)}
end
end
rule "FC003", "Check whether you are running with chef server before using server-specific features" do
tags %w{portability solo}
description "Ideally your cookbooks should be usable without requiring chef server."
recipe do |ast|
checks_for_chef_solo?(ast) ? [] : searches(ast).map{|s| match(s)}
end
end
rule "FC004", "Use a service resource to start and stop services" do
tags %w{style services}
description "Avoid use of execute to control services - use the service resource instead."
recipe do |ast|
find_resources(ast, 'execute').find_all do |cmd|
cmd_str = (resource_attribute('command', cmd) || resource_name(cmd)).to_s
cmd_str.include?('/etc/init.d') || cmd_str.start_with?('service ') || cmd_str.start_with?('/sbin/service ')
end.map{|cmd| match(cmd)}
end
end
rule "FC005", "Avoid repetition of resource declarations" do
tags %w{style}
description "Where you have a lot of resources that vary in only a single attribute wrap them in a loop for brevity."
recipe do |ast|
matches = []
# do all of the attributes for all resources of a given type match apart aside from one?
resource_attributes_by_type(ast).each do |type, resource_atts|
sorted_atts = resource_atts.map{|atts| atts.to_a.sort{|x,y| x.first.to_s <=> y.first.to_s }}
if sorted_atts.all?{|att| (att - sorted_atts.inject{|atts,a| atts & a}).length == 1}
matches << match(find_resources(ast, type).first)
end
end
matches
end
end
rule "FC006", "Mode should be quoted or fully specified when setting file permissions" do
tags %w{correctness files}
description "Not quoting mode when setting permissions can lead to incorrect permissions being set."
recipe do |ast|
ast.xpath(%q{//ident[@value='mode']/parent::command/descendant::int[string-length(@value) < 4]/
ancestor::method_add_block}).map{|resource| match(resource)}
end
end
rule "FC007", "Ensure recipe dependencies are reflected in cookbook metadata" do
tags %w{correctness metadata}
description "You are including a recipe that is not in the current cookbook and not defined as a dependency in your cookbook metadata."
recipe do |ast,filename|
metadata_path = Pathname.new(File.join(File.dirname(filename), '..', 'metadata.rb')).cleanpath
next unless File.exists? metadata_path
undeclared = included_recipes(ast).keys.map{|recipe|recipe.split('::').first} - [cookbook_name(filename)] -
declared_dependencies(read_file(metadata_path))
included_recipes(ast).map do |recipe, resource|
match(resource).merge(:filename => metadata_path) if undeclared.include?(recipe) || undeclared.any?{|u| recipe.start_with?("#{u}::")}
end.compact
end
end
rule "FC008", "Generated cookbook metadata needs updating" do
tags %w{style metadata}
description "The cookbook metadata for this cookbook is boilerplate output from knife generate cookbook and needs updating with the real details of your cookbook."
recipe do |ast,filename|
metadata_path = Pathname.new(File.join(File.dirname(filename), '..', 'metadata.rb')).cleanpath
next unless File.exists? metadata_path
md = read_file(metadata_path)
{'maintainer' => 'YOUR_COMPANY_NAME', 'maintainer_email' => 'YOUR_EMAIL'}.map do |field,value|
md.xpath(%Q{//command[ident/@value='#{field}']/descendant::tstring_content[@value='#{value}']}).map do |m|
match(m).merge(:filename => metadata_path)
end
end.flatten
end
end
rule "FC009", "Resource attribute not recognised" do
tags %w{correctness}
description "You appear to be using an unrecognised attribute on a standard Chef resource. Please check for typos."
recipe do |ast|
matches = []
resource_attributes_by_type(ast).each do |type,resources|
if Chef::Resource.const_defined?(convert_to_class_name(type))
allowed_atts = Chef::Resource.const_get(convert_to_class_name(type)).public_instance_methods(true)
resources.each do |resource|
invalid_atts = resource.keys.map{|att|att.to_sym} - allowed_atts
unless invalid_atts.empty?
matches << match(find_resources(ast, type).find{|res|resource_attributes(res).include?(invalid_atts.first.to_s)})
end
end
end
end
matches
end
end
rule "FC010", "Invalid search syntax" do
tags %w{correctness search}
description "The search expression in the recipe could not be parsed. Please check your syntax."
recipe do |ast|
# This only works for literal search strings
literal_searches(ast).reject{|search| valid_query?(search['value'])}.map{|search| match(search)}
end
end