puppet_auditor 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9f19c4e65cec6c68311283a645f82a70c5972713a8914998f90980c72ac46d9e
4
+ data.tar.gz: 2fcfd26e374196b17aae75102910375efa8f32b25672d2d5de2d4f12effce9e9
5
+ SHA512:
6
+ metadata.gz: 68f90d008056423702b399e106c75518ffe4e5b44f209a708239f557cc460665a96f7bc4fe4d5efce1b0e7368ef1818a6a4a806d7401e7eb6bf2f4d87bdb74dc
7
+ data.tar.gz: '08c51f70e10d0c21edf941cbb9a9414b0c73e7e8367fdf3f35e18897b25b07550e481243a724334b172e91a0cac12e7ccf48fe2f9765bba925ccd63814b7495e'
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in puppet_auditor.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,58 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ puppet_auditor (0.1.0)
5
+ puppet-lint (>= 1.1, < 3.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ coderay (1.1.2)
11
+ diff-lcs (1.3)
12
+ docile (1.3.0)
13
+ json (2.1.0)
14
+ method_source (0.9.0)
15
+ pry (0.11.3)
16
+ coderay (~> 1.1.0)
17
+ method_source (~> 0.9.0)
18
+ puppet-lint (2.3.5)
19
+ rake (10.5.0)
20
+ rspec (3.7.0)
21
+ rspec-core (~> 3.7.0)
22
+ rspec-expectations (~> 3.7.0)
23
+ rspec-mocks (~> 3.7.0)
24
+ rspec-collection_matchers (1.1.3)
25
+ rspec-expectations (>= 2.99.0.beta1)
26
+ rspec-core (3.7.1)
27
+ rspec-support (~> 3.7.0)
28
+ rspec-expectations (3.7.0)
29
+ diff-lcs (>= 1.2.0, < 2.0)
30
+ rspec-support (~> 3.7.0)
31
+ rspec-its (1.2.0)
32
+ rspec-core (>= 3.0.0)
33
+ rspec-expectations (>= 3.0.0)
34
+ rspec-mocks (3.7.0)
35
+ diff-lcs (>= 1.2.0, < 2.0)
36
+ rspec-support (~> 3.7.0)
37
+ rspec-support (3.7.1)
38
+ simplecov (0.16.1)
39
+ docile (~> 1.1)
40
+ json (>= 1.8, < 3)
41
+ simplecov-html (~> 0.10.0)
42
+ simplecov-html (0.10.2)
43
+
44
+ PLATFORMS
45
+ ruby
46
+
47
+ DEPENDENCIES
48
+ bundler (~> 1.16)
49
+ pry
50
+ puppet_auditor!
51
+ rake (~> 10.0)
52
+ rspec (~> 3.0)
53
+ rspec-collection_matchers (~> 1.0)
54
+ rspec-its (~> 1.0)
55
+ simplecov (~> 0.16)
56
+
57
+ BUNDLED WITH
58
+ 1.16.1
data/README.md ADDED
@@ -0,0 +1,89 @@
1
+ # PuppetAuditor
2
+
3
+ PuppetAuditor is a tool to test Puppet manifests against a set of defined rules.
4
+
5
+ ## Installation
6
+
7
+ Install PuppetAuditor with the gem command:
8
+
9
+ ```
10
+ $ gem install puppet_auditor
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ After the gem is installed, the tool can be used by calling:
16
+ ```
17
+ $ puppet_auditor
18
+ ```
19
+
20
+ Use the `--help` to check usage options.
21
+
22
+ The tool will check your code against the defined rules and display
23
+ messages if it finds any violation.
24
+
25
+ ## Defining rules
26
+
27
+ PuppetAuditor will attempt to load rules following this hierarchy:
28
+
29
+ - Rules defined in the host `/etc/puppet_auditor.yaml`
30
+ - Rules defined in the user home `~/.puppet_auditor.yaml`
31
+ - Rules defined in the project `$(pwd)/.puppet_auditor.yaml`
32
+
33
+ The yaml file with the rules should follow this format:
34
+
35
+ ```yaml
36
+ puppet_auditor_version: '1'
37
+ rules:
38
+ - name: Dangerous file config
39
+ resource: file
40
+ attributes:
41
+ recurse:
42
+ equals: true
43
+ ensure:
44
+ equals: present
45
+ message: Dont use recurse or ambiguous ensure
46
+ - name: Cant use latest
47
+ resource: package
48
+ attributes:
49
+ ensure:
50
+ equals: latest
51
+ message: Do not use latest
52
+ ```
53
+
54
+ The list of rules should declare individual rules with the following keys:
55
+
56
+ - `name`: a name for the defined rule
57
+ - `resource`: which resource should this rule verify
58
+ - `attributes`: an array of attributes that should be verified in this resource
59
+ - `message`: text that will appear if the rule is violated
60
+
61
+ The `attributes` value should follow this structre:
62
+
63
+ ```yaml
64
+ attribute:
65
+ comparison: value
66
+ ```
67
+
68
+ Where the `attribute` is a valid attribute for the valuated resource like "ensure" or "command",
69
+ `comparison` is one of the comparison function availables like "equals" or "matches" and the
70
+ `value` is the value that will be compared using the comparison function.
71
+
72
+ The following comparison functions are available:
73
+
74
+ - matches (regex)
75
+ - not_matches (regex)
76
+ - equals
77
+ - not_equal
78
+ - less_than
79
+ - less_or_equal_to
80
+ - greater_than
81
+ - greater_or_equal_to
82
+
83
+ For some samples check out the `spec/samples` folder.
84
+
85
+ ## How it works
86
+
87
+ The tool will parse rules defined in the `.puppet_auditor.yaml` files and if all rules are
88
+ valid it will dynamically generate [puppet-lint](https://github.com/rodjek/puppet-lint) plugins
89
+ and run them to check your code.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
4
+
5
+ require 'puppet_auditor'
6
+
7
+ exit PuppetAuditor::Cli.new(ARGV).run!
@@ -0,0 +1,48 @@
1
+ module PuppetAuditor
2
+ class Cli
3
+ def initialize(args)
4
+ @checks = []
5
+ OptionParser.new do |opts|
6
+ opts.banner = "Usage: puppet_auditor [options]"
7
+
8
+ host_default = true
9
+ user_default = true
10
+ project_default = true
11
+ specific_yaml_rules = nil
12
+
13
+ opts.on("--with-rules FILE", "Supply more rules from a specific yaml file") do |file|
14
+ specific_yaml_rules = file
15
+ end
16
+
17
+ opts.on("--no-host-defaults", "Do not atempt to load yaml rules from /etc/puppet_auditor.yaml") do
18
+ host_default = false
19
+ end
20
+
21
+ opts.on("--no-user-defaults", "Do not atempt to load yaml rules from ~/.puppet_auditor.yaml") do
22
+ user_default = false
23
+ end
24
+
25
+ opts.on("--no-project-defaults", "Do not atempt to load yaml rules from .puppet_auditor.yaml") do
26
+ project_default = false
27
+ end
28
+
29
+ load_checks('/etc/puppet_auditor.yaml') if host_default
30
+ load_checks('~/.puppet_auditor.yaml') if user_default && ENV.key?('HOME')
31
+ load_checks('.puppet_auditor.yaml') if project_default
32
+ load_checks(specific_yaml_rules) if specific_yaml_rules
33
+ end.parse!
34
+ end
35
+
36
+ def load_checks(filepath)
37
+ File.open(filepath) { |file| @checks << Loader.new(file).generate_checks } if File.readable?(filepath)
38
+ rescue PuppetAuditor::Error => err
39
+ puts "There was an error parsing rules from #{filepath}"
40
+ puts err.message
41
+ exit 1
42
+ end
43
+
44
+ def run!
45
+ PuppetLint::Bin.new([File.expand_path('.'), "--only-checks=#{@checks.join(',')}"]).run
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,4 @@
1
+ module PuppetAuditor
2
+ class Error < StandardError
3
+ end
4
+ end
@@ -0,0 +1,87 @@
1
+ module PuppetAuditor
2
+ class LintPlugin < PuppetLint::CheckPlugin
3
+
4
+ COMPARISONS = {
5
+ 'matches' => ->(expected, token) { token =~ expected },
6
+ 'not_matches' => ->(expected, token) { (token =~ expected).nil? },
7
+ 'equals' => ->(expected, token) { token == expected },
8
+ 'not_equal' => ->(expected, token) { token != expected },
9
+ 'less_than' => ->(expected, token) { token < expected },
10
+ 'less_or_equal_to' => ->(expected, token) { token <= expected },
11
+ 'greater_than' => ->(expected, token) { token > expected },
12
+ 'greater_or_equal_to' => ->(expected, token) { token >= expected },
13
+ }
14
+
15
+ def initialize
16
+ super
17
+ @resource = self.class::RESOURCE
18
+ @attributes = self.class::ATTRIBUTES
19
+ @message = self.class::MESSAGE
20
+ end
21
+
22
+ def check
23
+ resource_indexes.each { |resource| resource_block(resource) if resource[:type].value == @resource }
24
+ end
25
+
26
+ private
27
+
28
+ def resource_block(resource)
29
+ @attributes.each do |attribute_name, comparison_rules|
30
+ attribute = resource[:tokens].find { |t| t.type == :NAME && t.value == attribute_name && t.next_code_token.type == :FARROW }
31
+ attribute_block(resource, attribute, comparison_rules) if attribute
32
+ end
33
+ end
34
+
35
+ def attribute_block(resource, attribute, comparison_rules)
36
+ token = attribute.next_code_token.next_code_token
37
+ comparison_rules.each do |rule, value|
38
+ expected, token_value = cast_expected(rule, value), cast_token(token)
39
+ violation(resource, token) if COMPARISONS[rule].call(expected, token_value)
40
+ end
41
+ end
42
+
43
+ def cast_expected(rule, expected)
44
+ case rule
45
+ when 'matches', 'not_matches'
46
+ Regexp.new(expected)
47
+ else
48
+ expected
49
+ end
50
+ end
51
+
52
+ def cast_token(token)
53
+ case token.type
54
+ when :NUMBER
55
+ token.value.to_i
56
+ when :TRUE
57
+ true
58
+ when :FALSE
59
+ false
60
+ when :DQPRE # String with variables
61
+ full_str = token.value
62
+ next_token = token
63
+ while next_token.type != :DQPOST
64
+ next_token = next_token.next_code_token
65
+ if next_token.type == :VARIABLE
66
+ full_str += "${#{next_token.value}}"
67
+ else
68
+ full_str += next_token.value
69
+ end
70
+ end
71
+ full_str
72
+ else
73
+ token.value
74
+ end
75
+ end
76
+
77
+ def violation(resource, token)
78
+ notify :error, {
79
+ message: @message,
80
+ line: token.line,
81
+ column: token.column,
82
+ token: token,
83
+ resource: resource
84
+ }
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,46 @@
1
+ module PuppetAuditor
2
+ class Loader
3
+
4
+ REQUIRED_KEYS = ['resource', 'attributes', 'message']
5
+ VALID_COMPARISONS = LintPlugin::COMPARISONS.keys()
6
+
7
+ MUST_HAVE_RULES = "The rules yaml must have a rules key"
8
+ MUST_HAVE_NAME = "All rules must have the 'name' key"
9
+ RULE_WITHOUT_KEY = ->(name, key) { "Rule '#{name}' must have the '#{key}' key" }
10
+ INVALID_COMPARISON = ->(name, key) { "Rule '#{name}' have an invalid comparison: '#{key}'" }
11
+
12
+
13
+ def initialize(yaml_str, validate=true)
14
+ @spec = YAML.load(yaml_str)
15
+ validate! if validate
16
+ end
17
+
18
+ def validate!
19
+ raise Error.new(MUST_HAVE_RULES) unless @spec.include?('rules')
20
+ @spec['rules'].each do |rule|
21
+ raise Error.new(MUST_HAVE_NAME) unless rule.include?('name')
22
+ name = rule['name']
23
+ REQUIRED_KEYS.each { |key| raise Error.new(RULE_WITHOUT_KEY.call(name, key)) unless rule.include?(key) }
24
+ rule['attributes'].each do |attribute, comparisons|
25
+ comparisons.keys.each { |key| raise Error.new(INVALID_COMPARISON.call(name, key)) unless VALID_COMPARISONS.include?(key) }
26
+ end
27
+ end
28
+ end
29
+
30
+ def generate_checks
31
+ @spec['rules'].map do |rule|
32
+ rule_name = rule['name'].downcase.gsub(/[^a-z0-9\-_]+/i, '_')
33
+ rule_sym = rule_name.to_sym
34
+ class_name = rule_sym.to_s.split('_').map(&:capitalize).join
35
+ klass = PuppetLint.const_set("Check#{class_name}", Class.new(PuppetAuditor::LintPlugin))
36
+ klass.const_set('NAME', rule_sym)
37
+ klass.const_set('RESOURCE', rule['resource'])
38
+ klass.const_set('ATTRIBUTES', rule['attributes'])
39
+ klass.const_set('MESSAGE', rule['message'])
40
+ PuppetLint.configuration.add_check(rule_sym, klass)
41
+ PuppetLint::Data.ignore_overrides[rule_sym] ||= {}
42
+ rule_name
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,3 @@
1
+ module PuppetAuditor
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,7 @@
1
+ require "yaml"
2
+ require "optparse"
3
+ require "puppet-lint"
4
+ require "puppet_auditor/error"
5
+ require "puppet_auditor/lint_plugin"
6
+ require "puppet_auditor/loader"
7
+ require "puppet_auditor/cli"
@@ -0,0 +1,36 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "puppet_auditor/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "puppet_auditor"
8
+ spec.version = PuppetAuditor::VERSION
9
+ spec.authors = ["Oscar Esgalha"]
10
+ spec.email = ["oscar@instruct.com.br"]
11
+ spec.licenses = ['MIT']
12
+
13
+ spec.summary = %q{A tool to audit puppet manifests against a set of defined rules}
14
+ # spec.description = %q{TODO: Write a longer description or delete this line.}
15
+ spec.homepage = "https://github.com/instruct-br/puppet_auditor"
16
+
17
+ # `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^((test|spec|features)/|\..*|Jenkinsfile)}) }
18
+ spec.files = [
19
+ "Gemfile", "Gemfile.lock", "README.md", "Rakefile", "bin/puppet_auditor",
20
+ "lib/puppet_auditor.rb", "lib/puppet_auditor/cli.rb", "lib/puppet_auditor/error.rb",
21
+ "lib/puppet_auditor/lint_plugin.rb", "lib/puppet_auditor/loader.rb", "lib/puppet_auditor/version.rb",
22
+ "puppet_auditor.gemspec"
23
+ ]
24
+ spec.bindir = "bin"
25
+ spec.executables = ["puppet_auditor"]
26
+ spec.require_paths = ["lib"]
27
+
28
+ spec.add_dependency 'puppet-lint', '>= 1.1', '< 3.0'
29
+
30
+ spec.add_development_dependency "bundler", "~> 1.16"
31
+ spec.add_development_dependency "rake", "~> 10.0"
32
+ spec.add_development_dependency "rspec", "~> 3.0"
33
+ spec.add_development_dependency 'rspec-its', '~> 1.0'
34
+ spec.add_development_dependency 'rspec-collection_matchers', '~> 1.0'
35
+ spec.add_development_dependency "simplecov", "~> 0.16"
36
+ end
metadata ADDED
@@ -0,0 +1,161 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: puppet_auditor
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Oscar Esgalha
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-04-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: puppet-lint
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '1.1'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '3.0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '1.1'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '3.0'
33
+ - !ruby/object:Gem::Dependency
34
+ name: bundler
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.16'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.16'
47
+ - !ruby/object:Gem::Dependency
48
+ name: rake
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '10.0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '10.0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: rspec
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '3.0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '3.0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rspec-its
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '1.0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '1.0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: rspec-collection_matchers
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '1.0'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '1.0'
103
+ - !ruby/object:Gem::Dependency
104
+ name: simplecov
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '0.16'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '0.16'
117
+ description:
118
+ email:
119
+ - oscar@instruct.com.br
120
+ executables:
121
+ - puppet_auditor
122
+ extensions: []
123
+ extra_rdoc_files: []
124
+ files:
125
+ - Gemfile
126
+ - Gemfile.lock
127
+ - README.md
128
+ - Rakefile
129
+ - bin/puppet_auditor
130
+ - lib/puppet_auditor.rb
131
+ - lib/puppet_auditor/cli.rb
132
+ - lib/puppet_auditor/error.rb
133
+ - lib/puppet_auditor/lint_plugin.rb
134
+ - lib/puppet_auditor/loader.rb
135
+ - lib/puppet_auditor/version.rb
136
+ - puppet_auditor.gemspec
137
+ homepage: https://github.com/instruct-br/puppet_auditor
138
+ licenses:
139
+ - MIT
140
+ metadata: {}
141
+ post_install_message:
142
+ rdoc_options: []
143
+ require_paths:
144
+ - lib
145
+ required_ruby_version: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ required_rubygems_version: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ version: '0'
155
+ requirements: []
156
+ rubyforge_project:
157
+ rubygems_version: 2.7.6
158
+ signing_key:
159
+ specification_version: 4
160
+ summary: A tool to audit puppet manifests against a set of defined rules
161
+ test_files: []