inspec 0.9.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.
Files changed (247) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.rubocop.yml +65 -0
  4. data/.travis.yml +23 -0
  5. data/CHANGELOG.md +38 -0
  6. data/Gemfile +33 -0
  7. data/LICENSE +201 -0
  8. data/MAINTAINERS.md +28 -0
  9. data/MAINTAINERS.toml +42 -0
  10. data/README.md +257 -0
  11. data/Rakefile +47 -0
  12. data/bin/inspec +109 -0
  13. data/docs/ctl_inspec.rst +195 -0
  14. data/docs/dsl_inspec.rst +182 -0
  15. data/docs/readme.rst +100 -0
  16. data/docs/resources.rst +4319 -0
  17. data/docs/template.rst +51 -0
  18. data/examples/test-kitchen/.kitchen.yml +20 -0
  19. data/examples/test-kitchen/Berksfile +3 -0
  20. data/examples/test-kitchen/Gemfile +21 -0
  21. data/examples/test-kitchen/README.md +27 -0
  22. data/examples/test-kitchen/metadata.rb +7 -0
  23. data/examples/test-kitchen/recipes/default.rb +6 -0
  24. data/examples/test-kitchen/recipes/nginx.rb +30 -0
  25. data/examples/test-kitchen/test/integration/default/web_spec.rb +28 -0
  26. data/inspec.gemspec +30 -0
  27. data/lib/inspec.rb +20 -0
  28. data/lib/inspec/backend.rb +42 -0
  29. data/lib/inspec/dsl.rb +151 -0
  30. data/lib/inspec/log.rb +34 -0
  31. data/lib/inspec/metadata.rb +79 -0
  32. data/lib/inspec/plugins.rb +9 -0
  33. data/lib/inspec/plugins/resource.rb +62 -0
  34. data/lib/inspec/profile.rb +138 -0
  35. data/lib/inspec/profile_context.rb +170 -0
  36. data/lib/inspec/resource.rb +76 -0
  37. data/lib/inspec/rspec_json_formatter.rb +27 -0
  38. data/lib/inspec/rule.rb +170 -0
  39. data/lib/inspec/runner.rb +154 -0
  40. data/lib/inspec/shell.rb +66 -0
  41. data/lib/inspec/targets.rb +9 -0
  42. data/lib/inspec/targets/core.rb +27 -0
  43. data/lib/inspec/targets/dir.rb +67 -0
  44. data/lib/inspec/targets/file.rb +29 -0
  45. data/lib/inspec/targets/folder.rb +43 -0
  46. data/lib/inspec/targets/tar.rb +34 -0
  47. data/lib/inspec/targets/url.rb +39 -0
  48. data/lib/inspec/targets/zip.rb +47 -0
  49. data/lib/inspec/version.rb +7 -0
  50. data/lib/matchers/matchers.rb +221 -0
  51. data/lib/resources/apache.rb +29 -0
  52. data/lib/resources/apache_conf.rb +113 -0
  53. data/lib/resources/apt.rb +140 -0
  54. data/lib/resources/audit_policy.rb +63 -0
  55. data/lib/resources/auditd_conf.rb +56 -0
  56. data/lib/resources/auditd_rules.rb +53 -0
  57. data/lib/resources/bond.rb +65 -0
  58. data/lib/resources/bridge.rb +114 -0
  59. data/lib/resources/command.rb +57 -0
  60. data/lib/resources/csv.rb +32 -0
  61. data/lib/resources/directory.rb +15 -0
  62. data/lib/resources/etc_group.rb +150 -0
  63. data/lib/resources/file.rb +110 -0
  64. data/lib/resources/gem.rb +46 -0
  65. data/lib/resources/group.rb +132 -0
  66. data/lib/resources/host.rb +143 -0
  67. data/lib/resources/inetd_conf.rb +56 -0
  68. data/lib/resources/interface.rb +127 -0
  69. data/lib/resources/iptables.rb +65 -0
  70. data/lib/resources/json.rb +64 -0
  71. data/lib/resources/kernel_module.rb +40 -0
  72. data/lib/resources/kernel_parameter.rb +55 -0
  73. data/lib/resources/limits_conf.rb +55 -0
  74. data/lib/resources/login_def.rb +60 -0
  75. data/lib/resources/mysql.rb +81 -0
  76. data/lib/resources/mysql_conf.rb +116 -0
  77. data/lib/resources/mysql_session.rb +52 -0
  78. data/lib/resources/npm.rb +44 -0
  79. data/lib/resources/ntp_conf.rb +58 -0
  80. data/lib/resources/oneget.rb +63 -0
  81. data/lib/resources/os.rb +22 -0
  82. data/lib/resources/os_env.rb +34 -0
  83. data/lib/resources/package.rb +169 -0
  84. data/lib/resources/parse_config.rb +75 -0
  85. data/lib/resources/passwd.rb +93 -0
  86. data/lib/resources/pip.rb +75 -0
  87. data/lib/resources/port.rb +296 -0
  88. data/lib/resources/postgres.rb +37 -0
  89. data/lib/resources/postgres_conf.rb +87 -0
  90. data/lib/resources/postgres_session.rb +59 -0
  91. data/lib/resources/processes.rb +57 -0
  92. data/lib/resources/registry_key.rb +54 -0
  93. data/lib/resources/script.rb +34 -0
  94. data/lib/resources/security_policy.rb +73 -0
  95. data/lib/resources/service.rb +379 -0
  96. data/lib/resources/ssh_conf.rb +75 -0
  97. data/lib/resources/user.rb +374 -0
  98. data/lib/resources/windows_feature.rb +77 -0
  99. data/lib/resources/yaml.rb +23 -0
  100. data/lib/resources/yum.rb +154 -0
  101. data/lib/utils/convert.rb +12 -0
  102. data/lib/utils/detect.rb +15 -0
  103. data/lib/utils/find_files.rb +36 -0
  104. data/lib/utils/hash.rb +13 -0
  105. data/lib/utils/modulator.rb +12 -0
  106. data/lib/utils/parser.rb +61 -0
  107. data/lib/utils/simpleconfig.rb +115 -0
  108. data/tasks/maintainers.rb +213 -0
  109. data/test/docker_run.rb +156 -0
  110. data/test/docker_test.rb +51 -0
  111. data/test/helper.rb +200 -0
  112. data/test/integration/.kitchen.yml +42 -0
  113. data/test/integration/Berksfile +4 -0
  114. data/test/integration/cookbooks/os_prepare/metadata.rb +8 -0
  115. data/test/integration/cookbooks/os_prepare/recipes/apt.rb +20 -0
  116. data/test/integration/cookbooks/os_prepare/recipes/default.rb +9 -0
  117. data/test/integration/cookbooks/os_prepare/recipes/file.rb +21 -0
  118. data/test/integration/cookbooks/os_prepare/recipes/package.rb +26 -0
  119. data/test/integration/default/_debug_spec.rb +1 -0
  120. data/test/integration/default/apt_spec.rb +42 -0
  121. data/test/integration/default/file_spec.rb +109 -0
  122. data/test/integration/default/group_spec.rb +32 -0
  123. data/test/integration/default/kernel_module_spec.rb +17 -0
  124. data/test/integration/default/kernel_parameter_spec.rb +56 -0
  125. data/test/integration/default/package_spec.rb +11 -0
  126. data/test/integration/default/service_spec.rb +28 -0
  127. data/test/integration/default/user_spec.rb +44 -0
  128. data/test/resource/command_test.rb +33 -0
  129. data/test/resource/dsl_test.rb +45 -0
  130. data/test/resource/file_test.rb +130 -0
  131. data/test/resource/ssh_config.rb +9 -0
  132. data/test/resource/sshd_config.rb +9 -0
  133. data/test/test-extra.yaml +11 -0
  134. data/test/test.yaml +11 -0
  135. data/test/unit/mock/cmd/Get-NetAdapter +24 -0
  136. data/test/unit/mock/cmd/GetUserAccount +33 -0
  137. data/test/unit/mock/cmd/GetWin32Group +23 -0
  138. data/test/unit/mock/cmd/PATH +1 -0
  139. data/test/unit/mock/cmd/Resolve-DnsName +26 -0
  140. data/test/unit/mock/cmd/Test-NetConnection +4 -0
  141. data/test/unit/mock/cmd/auditctl +7 -0
  142. data/test/unit/mock/cmd/auditpol +2 -0
  143. data/test/unit/mock/cmd/brew-info-jq +1 -0
  144. data/test/unit/mock/cmd/chage-l-root +7 -0
  145. data/test/unit/mock/cmd/dpkg-s-curl +21 -0
  146. data/test/unit/mock/cmd/dscl +5 -0
  147. data/test/unit/mock/cmd/etc-apt +7 -0
  148. data/test/unit/mock/cmd/find-etc-rc-d-name-S +12 -0
  149. data/test/unit/mock/cmd/find-net-interface +9 -0
  150. data/test/unit/mock/cmd/gem-list-local-a-q-rubocop +1 -0
  151. data/test/unit/mock/cmd/get-net-tcpconnection +24 -0
  152. data/test/unit/mock/cmd/get-netadapter-binding-bridge +4 -0
  153. data/test/unit/mock/cmd/get-package-firefox +30 -0
  154. data/test/unit/mock/cmd/get-package-ruby +18 -0
  155. data/test/unit/mock/cmd/get-service-dhcp +10 -0
  156. data/test/unit/mock/cmd/get-windows-feature +7 -0
  157. data/test/unit/mock/cmd/getent-hosts-example.com +1 -0
  158. data/test/unit/mock/cmd/getent-passwd-root +1 -0
  159. data/test/unit/mock/cmd/id-chartmann +1 -0
  160. data/test/unit/mock/cmd/id-root +1 -0
  161. data/test/unit/mock/cmd/initctl-show-config-ssh +3 -0
  162. data/test/unit/mock/cmd/initctl-status-ssh +1 -0
  163. data/test/unit/mock/cmd/iptables-s +6 -0
  164. data/test/unit/mock/cmd/launchctl-list +3 -0
  165. data/test/unit/mock/cmd/ls-1-etc-init.d +2 -0
  166. data/test/unit/mock/cmd/ls-sys-class-net-br +2 -0
  167. data/test/unit/mock/cmd/lsmod +2 -0
  168. data/test/unit/mock/cmd/lsof-np-itcp +4 -0
  169. data/test/unit/mock/cmd/netstat-tulpen +5 -0
  170. data/test/unit/mock/cmd/npm-ls-g--json-bower +9 -0
  171. data/test/unit/mock/cmd/pacman-qi-curl +21 -0
  172. data/test/unit/mock/cmd/ping-example.com +6 -0
  173. data/test/unit/mock/cmd/pip-show-jinja2 +11 -0
  174. data/test/unit/mock/cmd/ps-aux +3 -0
  175. data/test/unit/mock/cmd/pw-usershow-root-7 +1 -0
  176. data/test/unit/mock/cmd/reg_schedule +1 -0
  177. data/test/unit/mock/cmd/rpm-qia-curl +24 -0
  178. data/test/unit/mock/cmd/sbin_sysctl +1 -0
  179. data/test/unit/mock/cmd/secedit-export +7 -0
  180. data/test/unit/mock/cmd/service-e +2 -0
  181. data/test/unit/mock/cmd/service-sendmail-onestatus +3 -0
  182. data/test/unit/mock/cmd/service-sshd-status +1 -0
  183. data/test/unit/mock/cmd/sockstat +5 -0
  184. data/test/unit/mock/cmd/success +0 -0
  185. data/test/unit/mock/cmd/systemctl-show-all-sshd +6 -0
  186. data/test/unit/mock/cmd/win32_product +8 -0
  187. data/test/unit/mock/cmd/yum-repolist-all +52 -0
  188. data/test/unit/mock/files/auditd.conf +4 -0
  189. data/test/unit/mock/files/bond0 +37 -0
  190. data/test/unit/mock/files/etcgroup +3 -0
  191. data/test/unit/mock/files/example.csv +6 -0
  192. data/test/unit/mock/files/inetd.conf +2 -0
  193. data/test/unit/mock/files/kitchen.yml +7 -0
  194. data/test/unit/mock/files/limits.conf +5 -0
  195. data/test/unit/mock/files/login.defs +5 -0
  196. data/test/unit/mock/files/mysql.conf +8 -0
  197. data/test/unit/mock/files/mysql2.conf +2 -0
  198. data/test/unit/mock/files/ntp.conf +5 -0
  199. data/test/unit/mock/files/passwd +2 -0
  200. data/test/unit/mock/files/policyfile.lock.json +12 -0
  201. data/test/unit/mock/files/ssh_config +5 -0
  202. data/test/unit/mock/files/sshd_config +7 -0
  203. data/test/unit/mock/profiles/empty/metadata.rb +0 -0
  204. data/test/unit/mock/profiles/metadata/metadata.rb +1 -0
  205. data/test/unit/profile_context_test.rb +140 -0
  206. data/test/unit/profile_test.rb +49 -0
  207. data/test/unit/resources/apt_test.rb +46 -0
  208. data/test/unit/resources/audit_policy_test.rb +13 -0
  209. data/test/unit/resources/auditd_conf_test.rb +15 -0
  210. data/test/unit/resources/auditd_rules_test.rb +21 -0
  211. data/test/unit/resources/bond_test.rb +24 -0
  212. data/test/unit/resources/bridge_test.rb +56 -0
  213. data/test/unit/resources/csv_test.rb +35 -0
  214. data/test/unit/resources/etc_group_test.rb +37 -0
  215. data/test/unit/resources/gem_test.rb +20 -0
  216. data/test/unit/resources/group_test.rb +96 -0
  217. data/test/unit/resources/host_test.rb +38 -0
  218. data/test/unit/resources/inetd_conf_test.rb +15 -0
  219. data/test/unit/resources/interface_test.rb +54 -0
  220. data/test/unit/resources/iptables_test.rb +30 -0
  221. data/test/unit/resources/json_test.rb +36 -0
  222. data/test/unit/resources/kernel_module_test.rb +23 -0
  223. data/test/unit/resources/kernel_parameter_test.rb +13 -0
  224. data/test/unit/resources/limits_conf_test.rb +14 -0
  225. data/test/unit/resources/login_def_test.rb +16 -0
  226. data/test/unit/resources/mysql_conf_test.rb +14 -0
  227. data/test/unit/resources/npm_test.rb +20 -0
  228. data/test/unit/resources/ntp_conf_test.rb +16 -0
  229. data/test/unit/resources/oneget_test.rb +45 -0
  230. data/test/unit/resources/os_env_test.rb +13 -0
  231. data/test/unit/resources/package_test.rb +51 -0
  232. data/test/unit/resources/passwd_test.rb +24 -0
  233. data/test/unit/resources/pip_test.rb +15 -0
  234. data/test/unit/resources/port_test.rb +46 -0
  235. data/test/unit/resources/processes_test.rb +32 -0
  236. data/test/unit/resources/registry_key_test.rb +19 -0
  237. data/test/unit/resources/script_test.rb +19 -0
  238. data/test/unit/resources/security_policy_test.rb +16 -0
  239. data/test/unit/resources/service_test.rb +116 -0
  240. data/test/unit/resources/ssh_conf_test.rb +33 -0
  241. data/test/unit/resources/user_test.rb +93 -0
  242. data/test/unit/resources/windows_feature.rb +17 -0
  243. data/test/unit/resources/yaml_test.rb +34 -0
  244. data/test/unit/resources/yum_test.rb +68 -0
  245. data/test/unit/simpleconfig_test.rb +80 -0
  246. data/test/unit/utils/content_parser_test.rb +30 -0
  247. metadata +555 -0
@@ -0,0 +1,65 @@
1
+ # encoding: utf-8
2
+ # author: Christoph Hartmann
3
+ # author: Dominik Richter
4
+
5
+ require 'resources/file'
6
+
7
+ # Usage:
8
+ # describe bond('bond0') do
9
+ # it { should exist }
10
+ # it { should have_interface 'eth0' }
11
+ # end
12
+
13
+ module Inspec::Resources
14
+ class Bond < File
15
+ name 'bond'
16
+
17
+ def initialize(bond)
18
+ @bond = bond
19
+ @path = "/proc/net/bonding/#{bond}"
20
+ @file = inspec.file(@path)
21
+ @content = nil
22
+ @params = {}
23
+ @loaded = false
24
+ end
25
+
26
+ def read_content
27
+ # parse the file
28
+ @content = @file.content
29
+ @params = SimpleConfig.new(
30
+ @file.content,
31
+ assignment_re: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
32
+ multiple_values: true,
33
+ ).params if @file.exist?
34
+ @loaded = true
35
+ @content
36
+ end
37
+
38
+ # ensures the content is loaded before we return the params
39
+ def params
40
+ read_content if @loaded == false
41
+ @params
42
+ end
43
+
44
+ def content
45
+ read_content if @loaded == false
46
+ @content
47
+ end
48
+
49
+ def exist?
50
+ @file.exist?
51
+ end
52
+
53
+ def has_interface?(interface)
54
+ params['Slave Interface'].include?(interface)
55
+ end
56
+
57
+ def interfaces
58
+ params['Slave Interface']
59
+ end
60
+
61
+ def to_s
62
+ "Bond #{@bond}"
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,114 @@
1
+ # encoding: utf-8
2
+ # author: Christoph Hartmann
3
+ # author: Dominik Richter
4
+
5
+ # Usage:
6
+ # describe bridge('br0') do
7
+ # it { should exist }
8
+ # it { should have_interface 'eth0' }
9
+ # end
10
+
11
+ class Bridge < Inspec.resource(1)
12
+ name 'bridge'
13
+
14
+ def initialize(bridge_name)
15
+ @bridge_name = bridge_name
16
+
17
+ @bridge_provider = nil
18
+ if inspec.os.linux?
19
+ @bridge_provider = LinuxBridge.new(inspec)
20
+ elsif inspec.os.windows?
21
+ @bridge_provider = WindowsBridge.new(inspec)
22
+ else
23
+ return skip_resource 'The `bridge` resource is not supported on your OS yet.'
24
+ end
25
+ end
26
+
27
+ def exists?
28
+ !bridge_info.nil? && !bridge_info[:name].nil?
29
+ end
30
+
31
+ def has_interface?(interface)
32
+ return skip_resource 'The `bridge` resource does not provide interface detection for Windows yet' if inspec.os.windows?
33
+ bridge_info.nil? ? false : bridge_info[:interfaces].include?(interface)
34
+ end
35
+
36
+ def interfaces
37
+ bridge_info.nil? ? nil : bridge_info[:interfaces]
38
+ end
39
+
40
+ def to_s
41
+ "Bridge #{@bridge_name}"
42
+ end
43
+
44
+ private
45
+
46
+ def bridge_info
47
+ return @cache if defined?(@cache)
48
+ @cache = @bridge_provider.bridge_info(@bridge_name) if !@bridge_provider.nil?
49
+ end
50
+ end
51
+
52
+ class BridgeDetection
53
+ attr_reader :inspec
54
+ def initialize(inspec)
55
+ @inspec = inspec
56
+ end
57
+ end
58
+
59
+ # Linux Bridge
60
+ # If /sys/class/net/{interface}/bridge exists then it must be a bridge
61
+ # /sys/class/net/{interface}/brif contains the network interfaces
62
+ # @see http://www.tldp.org/HOWTO/BRIDGE-STP-HOWTO/set-up-the-bridge.html
63
+ # @see http://unix.stackexchange.com/questions/40560/how-to-know-if-a-network-interface-is-tap-tun-bridge-or-physical
64
+ class LinuxBridge < BridgeDetection
65
+ def bridge_info(bridge_name)
66
+ # read bridge information
67
+ bridge = inspec.file("/sys/class/net/#{bridge_name}/bridge").directory?
68
+ return nil unless bridge
69
+
70
+ # load interface names
71
+ interfaces = inspec.command("ls -1 /sys/class/net/#{bridge_name}/brif/")
72
+ interfaces = interfaces.stdout.chomp.split("\n")
73
+ {
74
+ name: bridge_name,
75
+ interfaces: interfaces,
76
+ }
77
+ end
78
+ end
79
+
80
+ # Windows Bridge
81
+ # select netadapter by adapter binding for windows
82
+ # Get-NetAdapterBinding -ComponentID ms_bridge | Get-NetAdapter
83
+ # @see https://technet.microsoft.com/en-us/library/jj130921(v=wps.630).aspx
84
+ # RegKeys: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}
85
+ class WindowsBridge < BridgeDetection
86
+ def bridge_info(bridge_name)
87
+ # find all bridge adapters
88
+ cmd = inspec.command('Get-NetAdapterBinding -ComponentID ms_bridge | Get-NetAdapter | Select-Object -Property Name, InterfaceDescription | ConvertTo-Json')
89
+
90
+ # filter network interface
91
+ begin
92
+ bridges = JSON.parse(cmd.stdout)
93
+ rescue JSON::ParserError => _e
94
+ return nil
95
+ end
96
+
97
+ # ensure we have an array of groups
98
+ bridges = [bridges] if !bridges.is_a?(Array)
99
+
100
+ # select the requested interface
101
+ bridges = bridges.each_with_object([]) do |adapter, adapter_collection|
102
+ # map object
103
+ info = {
104
+ name: adapter['Name'],
105
+ interfaces: nil,
106
+ }
107
+ adapter_collection.push(info) if info[:name].casecmp(bridge_name) == 0
108
+ end
109
+
110
+ return nil if bridges.size == 0
111
+ warn "[Possible Error] detected multiple bridges interfaces with the name #{bridge_name}" if bridges.size > 1
112
+ bridges[0]
113
+ end
114
+ end
@@ -0,0 +1,57 @@
1
+ # encoding: utf-8
2
+ # copyright: 2015, Vulcano Security GmbH
3
+ # author: Dominik Richter
4
+ # author: Christoph Hartmann
5
+ # license: All rights reserved
6
+
7
+ # Usage:
8
+ # describe command('ls -al /') do
9
+ # it { should exist }
10
+ # its(:stdout) { should match /bin/ }
11
+ # its(:stderr) { should match /No such file or directory/ }
12
+ # its(:exit_status) { should eq 0 }
13
+ # end
14
+
15
+ class Cmd < Inspec.resource(1)
16
+ name 'command'
17
+ def initialize(cmd)
18
+ @command = cmd
19
+ end
20
+
21
+ def result
22
+ @result ||= inspec.backend.run_command(@command)
23
+ end
24
+
25
+ def stdout
26
+ result.stdout
27
+ end
28
+
29
+ def stderr
30
+ result.stderr
31
+ end
32
+
33
+ def exit_status
34
+ result.exit_status.to_i
35
+ end
36
+
37
+ def exist?
38
+ if inspec.os.linux?
39
+ res = inspec.backend.run_command("bash -c 'type \"#{@command}\"'")
40
+ elsif inspec.os.windows?
41
+ res = inspec.backend.run_command("where.exe \"#{@command}\"")
42
+ elsif inspec.os.unix?
43
+ res = inspec.backend.run_command("type \"#{@command}\"")
44
+ elsif inspec.os[:family].to_s == 'unknown'
45
+ # silent for mock resources
46
+ return false
47
+ else
48
+ warn "`command(#{@command}).exist?` is not suported on you OS: #{inspec.os[:family]}"
49
+ return false
50
+ end
51
+ res.exit_status.to_i == 0
52
+ end
53
+
54
+ def to_s
55
+ "Command #{@command}"
56
+ end
57
+ end
@@ -0,0 +1,32 @@
1
+ # encoding: utf-8
2
+ # author: Christoph Hartmann
3
+ # author: Dominik Richter
4
+
5
+ # Parses a csv document
6
+ # Usage:
7
+ # describe csv('example.csv') do
8
+ # its('name') { should eq(['John', 'Alice']) }
9
+ # end
10
+ #
11
+ # This implementation was inspired by a blog post
12
+ # @see http://technicalpickles.com/posts/parsing-csv-with-ruby
13
+ class CsvConfig < JsonConfig
14
+ name 'csv'
15
+
16
+ # override file load and parse hash from csv
17
+ def parse(content)
18
+ require 'csv'
19
+ # convert empty field to nil
20
+ CSV::Converters[:blank_to_nil] = lambda do |field|
21
+ field && field.empty? ? nil : field
22
+ end
23
+ # implicit conversion of values
24
+ csv = CSV.new(content, headers: true, converters: [:all, :blank_to_nil])
25
+ # convert to hash
26
+ csv.to_a.map(&:to_hash)
27
+ end
28
+
29
+ def to_s
30
+ "Csv #{@path}"
31
+ end
32
+ end
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+ # author: Dominik Richter
3
+ # author: Christoph Hartmann
4
+
5
+ require 'resources/file'
6
+
7
+ module Inspec::Resources
8
+ class Directory < File
9
+ name 'directory'
10
+ end
11
+
12
+ def to_s
13
+ "Directory #{@path}"
14
+ end
15
+ end
@@ -0,0 +1,150 @@
1
+ # encoding: utf-8
2
+ # copyright: 2015, Vulcano Security GmbH
3
+ # author: Christoph Hartmann
4
+ # author: Dominik Richter
5
+ # license: All rights reserved
6
+
7
+ # The file format consists of
8
+ # - group name
9
+ # - password - group's encrypted password
10
+ # - gid - group's decimal ID
11
+ # - member list - group members, comma seperated list
12
+ #
13
+ # Usage:
14
+ # describe etc_group do
15
+ # its('gids') { should_not contain_duplicates }
16
+ # its('groups') { should include 'my_user' }
17
+ # its('users') { should include 'my_user' }
18
+ # end
19
+ #
20
+ # describe etc_group.where(name: 'my_group') do
21
+ # its('users') { should include 'my_user' }
22
+ # end
23
+
24
+ require 'utils/convert'
25
+ require 'utils/parser'
26
+
27
+ class EtcGroup < Inspec.resource(1)
28
+ include Converter
29
+ include ContentParser
30
+
31
+ name 'etc_group'
32
+
33
+ attr_accessor :gid, :entries
34
+ def initialize(path = nil)
35
+ @path = path || '/etc/group'
36
+ @entries = parse_group(@path)
37
+
38
+ # skip resource if it is not supported on current OS
39
+ return skip_resource 'The `etc_group` resource is not supported on your OS.' \
40
+ unless %w{ubuntu debian redhat fedora arch darwin freebsd}.include?(inspec.os[:family])
41
+ end
42
+
43
+ def groups(filter = nil)
44
+ entries = filter || @entries
45
+ entries.map { |x| x['name'] } if !entries.nil?
46
+ end
47
+
48
+ def gids(filter = nil)
49
+ entries = filter || @entries
50
+ entries.map { |x| x['gid'] } if !entries.nil?
51
+ end
52
+
53
+ def users(filter = nil)
54
+ entries = filter || @entries
55
+ return nil if entries.nil?
56
+ # filter the user entry
57
+ res = entries.map { |x|
58
+ x['members'].split(',') if !x.nil? && !x['members'].nil?
59
+ }.flatten
60
+ # filter nil elements
61
+ res.reject { |x| x.nil? || x.empty? }
62
+ end
63
+
64
+ def where(conditions = {})
65
+ return if conditions.empty?
66
+ fields = {
67
+ name: 'name',
68
+ group_name: 'name',
69
+ password: 'password',
70
+ gid: 'gid',
71
+ group_id: 'gid',
72
+ users: 'members',
73
+ members: 'members',
74
+ }
75
+ res = entries
76
+
77
+ conditions.each do |k, v|
78
+ idx = fields[k.to_sym]
79
+ next if idx.nil?
80
+ res = res.select { |x| x[idx] == v.to_s }
81
+ end
82
+
83
+ EtcGroupView.new(self, res)
84
+ end
85
+
86
+ def to_s
87
+ '/etc/group'
88
+ end
89
+
90
+ private
91
+
92
+ def parse_group(path)
93
+ @content = inspec.file(path).content
94
+ if @content.nil?
95
+ skip_resource "Can't access group file in #{path}"
96
+ return []
97
+ end
98
+ # iterate over each line and filter comments
99
+ @content.split("\n").each_with_object([]) do |line, lines|
100
+ grp_info = parse_group_line(line)
101
+ lines.push(grp_info) if !grp_info.nil? && grp_info.size > 0
102
+ end
103
+ end
104
+
105
+ def parse_group_line(line)
106
+ opts = {
107
+ comment_char: '#',
108
+ standalone_comments: false,
109
+ }
110
+ line, _idx_nl = parse_comment_line(line, opts)
111
+ x = line.split(':')
112
+ # abort if we have an empty or comment line
113
+ return nil if x.size == 0
114
+ # map data
115
+ {
116
+ 'name' => x.at(0), # Name of the group.
117
+ 'password' => x.at(1), # Group's encrypted password.
118
+ 'gid' => convert_to_i(x.at(2)), # The group's decimal ID.
119
+ 'members' => x.at(3), # Group members.
120
+ }
121
+ end
122
+ end
123
+
124
+ # object that hold a specifc view on etc group
125
+ class EtcGroupView
126
+ def initialize(parent, filter)
127
+ @parent = parent
128
+ @filter = filter
129
+ end
130
+
131
+ # returns the group object
132
+ def entries
133
+ @filter
134
+ end
135
+
136
+ # only returns group name
137
+ def groups
138
+ @parent.groups(@filter)
139
+ end
140
+
141
+ # only return gids
142
+ def gids
143
+ @parent.gids(@filter)
144
+ end
145
+
146
+ # only returns users
147
+ def users
148
+ @parent.users(@filter)
149
+ end
150
+ end
@@ -0,0 +1,110 @@
1
+ # encoding: utf-8
2
+ # copyright: 2015, Vulcano Security GmbH
3
+ # author: Dominik Richter
4
+ # author: Christoph Hartmann
5
+ # license: All rights reserved
6
+
7
+ module Inspec::Resources
8
+ class File < Inspec.resource(1)
9
+ name 'file'
10
+
11
+ attr_reader :path
12
+ def initialize(path)
13
+ @path = path
14
+ @file = inspec.backend.file(@path)
15
+ end
16
+
17
+ %w{
18
+ type exist? file? block_device? character_device? socket? directory?
19
+ symlink? pipe? mode mode? owner owned_by? group grouped_into? link_target
20
+ link_path linked_to? content mtime size selinux_label mounted? immutable?
21
+ product_version file_version version? md5sum sha256sum
22
+ }.each do |m|
23
+ define_method m.to_sym do |*args|
24
+ @file.method(m.to_sym).call(*args)
25
+ end
26
+ end
27
+
28
+ def contain(*_)
29
+ fail 'Contain is not supported. Please use standard RSpec matchers.'
30
+ end
31
+
32
+ def readable?(by_owner, by_user)
33
+ if inspec.os.unix?
34
+ by_owner, by_user = check_preconditions(by_owner, by_user)
35
+
36
+ if by_user.nil?
37
+ m = @file.unix_mode_mask(by_owner, 'r') ||
38
+ fail("#{by_owner} is not a valid unix owner.")
39
+ (@file.mode & m) != 0
40
+ else
41
+ check_user_access(by_user, @path, 'r')
42
+ end
43
+ else
44
+ fail "`file(#{@path}).executable?` is not suported on you OS: #{inspec.os['family']}"
45
+ end
46
+ end
47
+
48
+ def writable?(by_owner, by_user)
49
+ if inspec.os.unix?
50
+ by_owner, by_user = check_preconditions(by_owner, by_user)
51
+
52
+ if by_user.nil?
53
+ m = @file.unix_mode_mask(by_owner, 'w') ||
54
+ fail("#{by_owner} is not a valid unix owner.")
55
+ (@file.mode & m) != 0
56
+ else
57
+ check_user_access(by_user, @path, 'w')
58
+ end
59
+ else
60
+ fail "`file(#{@path}).executable?` is not suported on you OS: #{inspec.os['family']}"
61
+ end
62
+ end
63
+
64
+ def executable?(by_owner, by_user)
65
+ if inspec.os.unix?
66
+ by_owner, by_user = check_preconditions(by_owner, by_user)
67
+
68
+ if by_user.nil?
69
+ m = @file.unix_mode_mask(by_owner, 'x') ||
70
+ fail("#{by_owner} is not a valid unix owner.")
71
+ return (@file.mode & m) != 0
72
+ else
73
+ return check_user_access(by_user, @path, 'x')
74
+ end
75
+ else
76
+ fail "`file(#{@path}).executable?` is not suported on you OS: #{inspec.os['family']}"
77
+ end
78
+ end
79
+
80
+ def to_s
81
+ "File #{@path}"
82
+ end
83
+
84
+ private
85
+
86
+ def check_preconditions(by_owner, by_user)
87
+ by_owner = 'other' if by_owner == 'others'
88
+ by_owner = 'all' if (by_owner.nil? || by_owner.empty?) && (by_user.nil?)
89
+ [by_owner, by_user]
90
+ end
91
+
92
+ # check permissions on linux
93
+ def check_user_access(user, file, flag)
94
+ if inspec.os.linux? == true
95
+ # use sh on linux
96
+ perm_cmd = "su -s /bin/sh -c \"test -#{flag} #{file}\" #{user}"
97
+ elsif inspec.os[:family] == 'freebsd'
98
+ # use sudo on freebsd
99
+ perm_cmd = "sudo -u #{user} test -#{flag} #{file}"
100
+ end
101
+
102
+ if !perm_cmd.nil?
103
+ cmd = inspec.command(perm_cmd)
104
+ cmd.exit_status == 0 ? true : false
105
+ else
106
+ return skip_resource 'The `file` resource does not support `by_user` on your OS.'
107
+ end
108
+ end
109
+ end
110
+ end