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,46 @@
1
+ # encoding: utf-8
2
+ # author: Christoph Hartmann
3
+ # author: Dominik Richter
4
+
5
+ # Usage:
6
+ # describe gem('rubocop') do
7
+ # it { should be_installed }
8
+ # end
9
+ class GemPackage < Inspec.resource(1)
10
+ name 'gem'
11
+
12
+ def initialize(package_name)
13
+ @package_name = package_name
14
+ end
15
+
16
+ def info
17
+ return @info if defined?(@info)
18
+
19
+ cmd = inspec.command("gem list --local -a -q \^#{@package_name}\$")
20
+ @info = {
21
+ installed: cmd.exit_status == 0,
22
+ type: 'gem',
23
+ }
24
+ return @info unless @info[:installed]
25
+
26
+ # extract package name and version
27
+ # parses data like winrm (1.3.4, 1.3.3)
28
+ params = /^\s*([^\(]*?)\s*\((.*?)\)\s*$/.match(cmd.stdout.chomp)
29
+ versions = params[2].split(',')
30
+ @info[:name] = params[1]
31
+ @info[:version] = versions[0]
32
+ @info
33
+ end
34
+
35
+ def installed?
36
+ info[:installed] == true
37
+ end
38
+
39
+ def version
40
+ info[:version]
41
+ end
42
+
43
+ def to_s
44
+ "gem package #{@package_name}"
45
+ end
46
+ end
@@ -0,0 +1,132 @@
1
+ # encoding: utf-8
2
+ # author: Christoph Hartmann
3
+ # author: Dominik Richter
4
+
5
+ # Usage:
6
+ # describe group('root') do
7
+ # it { should exist }
8
+ # its('gid') { should eq 0 }
9
+ # end
10
+ #
11
+ # deprecated has matcher
12
+ # describe group('root') do
13
+ # it { should have_gid 0 }
14
+ # end
15
+
16
+ class Group < Inspec.resource(1)
17
+ name 'group'
18
+
19
+ def initialize(groupname, domain = nil)
20
+ @group = groupname.downcase
21
+ @domain = domain
22
+ @domain = @domain.downcase unless @domain.nil?
23
+
24
+ @cache = nil
25
+
26
+ # select group manager
27
+ @group_provider = nil
28
+ if inspec.os.unix?
29
+ @group_provider = UnixGroup.new(inspec)
30
+ elsif inspec.os.windows?
31
+ @group_provider = WindowsGroup.new(inspec)
32
+ else
33
+ return skip_resource 'The `group` resource is not supported on your OS yet.'
34
+ end
35
+ end
36
+
37
+ # verifies if a group exists
38
+ def exists?
39
+ # ensure that we found one group
40
+ !group_info.nil? && group_info.size > 0
41
+ end
42
+
43
+ def gid
44
+ if group_info.nil? || group_info.size == 0
45
+ return nil
46
+ elsif group_info.size == 1
47
+ # the default case should be one group
48
+ return group_info[0][:gid]
49
+ else
50
+ # return array if we got multiple gids
51
+ return group_info.map { |grp| grp[:gid] }
52
+ end
53
+ end
54
+
55
+ # implements rspec has matcher, to be compatible with serverspec
56
+ def has_gid?(compare_gid)
57
+ gid == compare_gid
58
+ end
59
+
60
+ def local
61
+ if group_info.nil? || group_info.size == 0
62
+ return nil
63
+ elsif group_info.size == 1
64
+ # the default case should be one group
65
+ return group_info[0][:local]
66
+ else
67
+ # return array if we got multiple gids
68
+ return group_info.map { |grp| grp[:local] }
69
+ end
70
+ end
71
+
72
+ def to_s
73
+ "Group #{@group}"
74
+ end
75
+
76
+ private
77
+
78
+ def group_info
79
+ return @cache if !@cache.nil?
80
+ @cache = @group_provider.group_info(@group, @domain) if !@group_provider.nil?
81
+ end
82
+ end
83
+
84
+ class GroupInfo
85
+ attr_reader :inspec
86
+ def initialize(inspec)
87
+ @inspec = inspec
88
+ end
89
+ end
90
+
91
+ # implements generic unix groups via /etc/group
92
+ class UnixGroup < GroupInfo
93
+ def group_info(group, _domain = nil)
94
+ inspec.etc_group.where(name: group).entries.map { |grp|
95
+ {
96
+ name: grp['name'],
97
+ gid: grp['gid'],
98
+ }
99
+ }
100
+ end
101
+ end
102
+
103
+ class WindowsGroup < GroupInfo
104
+ def group_info(compare_group, compare_domain = nil)
105
+ cmd = inspec.command('Get-WmiObject Win32_Group | Select-Object -Property Caption, Domain, Name, SID, LocalAccount | ConvertTo-Json')
106
+
107
+ # cannot rely on exit code for now, successful command returns exit code 1
108
+ # return nil if cmd.exit_status != 0, try to parse json
109
+ begin
110
+ groups = JSON.parse(cmd.stdout)
111
+ rescue JSON::ParserError => _e
112
+ return nil
113
+ end
114
+
115
+ # ensure we have an array of groups
116
+ groups = [groups] if !groups.is_a?(Array)
117
+
118
+ # reduce list
119
+ groups.each_with_object([]) do |grp, grp_collection|
120
+ # map object
121
+ grp_info = {
122
+ name: grp['Name'],
123
+ domain: grp['Domain'],
124
+ caption: grp['Caption'],
125
+ gid: nil,
126
+ sid: grp['SID'],
127
+ local: grp['LocalAccount'],
128
+ }
129
+ return grp_collection.push(grp_info) if grp_info[:name].casecmp(compare_group) == 0 && (compare_domain.nil? || grp_info[:domain].casecmp(compare_domain) == 0)
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,143 @@
1
+ # encoding: utf-8
2
+ # author: Christoph Hartmann
3
+ # author: Dominik Richter
4
+
5
+ # Usage:
6
+ # describe host('example.com') do
7
+ # it { should be_resolvable }
8
+ # it { should be_reachable }
9
+ # its(:ipaddress) { should include '93.184.216.34' }
10
+ # end
11
+ #
12
+ # To verify a hostname with protocol and port
13
+ # describe host('example.com', port: 53, proto: 'udp') do
14
+ # it { should be_reachable }
15
+ # end
16
+ #
17
+ # We do not support the following serverspec syntax:
18
+ # describe host('example.com') do
19
+ # it { should be_reachable.with( :port => 22 ) }
20
+ # it { should be_reachable.with( :port => 22, :proto => 'tcp' ) }
21
+ # it { should be_reachable.with( :port => 53, :proto => 'udp' ) }
22
+ #
23
+ # it { should be_resolvable.by('hosts') }
24
+ # it { should be_resolvable.by('dns') }
25
+ # end
26
+
27
+ class Host < Inspec.resource(1)
28
+ name 'host'
29
+
30
+ def initialize(hostname, params = {})
31
+ @hostname = hostname
32
+ @port = params[:port] || nil
33
+ @proto = params[:proto] || nil
34
+
35
+ @host_provider = nil
36
+ if inspec.os.linux?
37
+ @host_provider = LinuxHostProvider.new(inspec)
38
+ elsif inspec.os.windows?
39
+ @host_provider = WindowsHostProvider.new(inspec)
40
+ else
41
+ return skip_resource 'The `host` resource is not supported on your OS yet.'
42
+ end
43
+ end
44
+
45
+ # if we get the IP adress, the host is resolvable
46
+ def resolvable?(type = nil)
47
+ warn "The `host` resource ignores #{type} parameters. Continue to resolve host." if !type.nil?
48
+ resolve.nil? || resolve.empty? ? false : true
49
+ end
50
+
51
+ def reachable?(port = nil, proto = nil, timeout = nil)
52
+ fail "Use `host` resource with host('#{@hostname}', port: #{port}, proto: '#{proto}') parameters." if !port.nil? || !proto.nil? || !timeout.nil?
53
+ ping.nil? ? false : ping
54
+ end
55
+
56
+ # returns all A records of the IP adress, will return an array
57
+ def ipaddress
58
+ resolve.nil? || resolve.empty? ? nil : resolve
59
+ end
60
+
61
+ def to_s
62
+ "Host #{@hostname}"
63
+ end
64
+
65
+ private
66
+
67
+ def ping
68
+ return @ping_cache if defined?(@ping_cache)
69
+ @ping_cache = @host_provider.ping(@hostname, @port, @proto) if !@host_provider.nil?
70
+ end
71
+
72
+ def resolve
73
+ return @ip_cache if defined?(@ip_cache)
74
+ @ip_cache = @host_provider.resolve(@hostname) if !@host_provider.nil?
75
+ end
76
+ end
77
+
78
+ class HostProvider
79
+ attr_reader :inspec
80
+ def initialize(inspec)
81
+ @inspec = inspec
82
+ end
83
+ end
84
+
85
+ class LinuxHostProvider < HostProvider
86
+ # ping is difficult to achieve, since we are not sure
87
+ def ping(hostname, _port = nil, _proto = nil)
88
+ # fall back to ping, but we can only test ICMP packages with ping
89
+ # therefore we have to skip the test, if we do not have everything on the node to run the test
90
+ ping = inspec.command("ping -w 1 -c 1 #{hostname}")
91
+ ping.exit_status.to_i != 0 ? false : true
92
+ end
93
+
94
+ def resolve(hostname)
95
+ # TODO: we rely on getent hosts for now, but it prefers to return IPv6, only then IPv4
96
+ cmd = inspec.command("getent hosts #{hostname}")
97
+ return nil if cmd.exit_status.to_i != 0
98
+
99
+ # extract ip adress
100
+ resolve = /^\s*(?<ip>\S+)\s+(.*)\s*$/.match(cmd.stdout.chomp)
101
+ [resolve[1]] if resolve
102
+ end
103
+ end
104
+
105
+ # Windows
106
+ # TODO: UDP is not supported yey, we need a custom ps1 script to add udp support
107
+ # @see http://blogs.technet.com/b/josebda/archive/2015/04/18/windows-powershell-equivalents-for-common-networking-commands-ipconfig-ping-nslookup.aspx
108
+ # @see http://blogs.technet.com/b/heyscriptingguy/archive/2014/03/19/creating-a-port-scanner-with-windows-powershell.aspx
109
+ class WindowsHostProvider < HostProvider
110
+ def ping(hostname, port = nil, proto = nil)
111
+ # TODO: abort if we cannot run it via udp
112
+ return nil if proto == 'udp'
113
+
114
+ # ICMP: Test-NetConnection www.microsoft.com
115
+ # TCP and port: Test-NetConnection -ComputerName www.microsoft.com -RemotePort 80
116
+ request = "Test-NetConnection -ComputerName #{hostname}"
117
+ request += " -RemotePort #{port}" unless port.nil?
118
+ request += '| Select-Object -Property ComputerName, RemoteAddress, RemotePort, SourceAddress, PingSucceeded | ConvertTo-Json'
119
+ p request
120
+ request += '| Select-Object -Property ComputerName, PingSucceeded | ConvertTo-Json'
121
+ cmd = inspec.command(request)
122
+
123
+ begin
124
+ ping = JSON.parse(cmd.stdout)
125
+ rescue JSON::ParserError => _e
126
+ return nil
127
+ end
128
+
129
+ ping['PingSucceeded']
130
+ end
131
+
132
+ def resolve(hostname)
133
+ cmd = inspec.command("Resolve-DnsName –Type A #{hostname} | ConvertTo-Json")
134
+ begin
135
+ resolv = JSON.parse(cmd.stdout)
136
+ rescue JSON::ParserError => _e
137
+ return nil
138
+ end
139
+
140
+ resolv = [resolv] unless resolv.is_a?(Array)
141
+ resolv.map { |entry| entry['IPAddress'] }
142
+ end
143
+ end
@@ -0,0 +1,56 @@
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
+ require 'utils/simpleconfig'
8
+
9
+ # Usage:
10
+ #
11
+ # describe inetd_conf do
12
+ # its('shell') { should eq nil }
13
+ # its('login') { should eq nil }
14
+ # its('exec') { should eq nil }
15
+ # end
16
+
17
+ class InetdConf < Inspec.resource(1)
18
+ name 'inetd_conf'
19
+
20
+ def initialize(path = nil)
21
+ @conf_path = path || '/etc/inetd.conf'
22
+ end
23
+
24
+ def method_missing(name)
25
+ read_params[name.to_s]
26
+ end
27
+
28
+ def read_params
29
+ return @params if defined?(@params)
30
+
31
+ # read the file
32
+ file = inspec.file(@conf_path)
33
+ if !file.file?
34
+ skip_resource "Can't find file \"#{@conf_path}\""
35
+ return @params = {}
36
+ end
37
+
38
+ content = file.content
39
+ if content.empty? && file.size > 0
40
+ skip_resource "Can't read file \"#{@conf_path}\""
41
+ return @params = {}
42
+ end
43
+ # parse the file
44
+ conf = SimpleConfig.new(
45
+ content,
46
+ assignment_re: /^\s*(\S+?)\s+(.*?)\s+(.*?)\s+(.*?)\s+(.*?)\s+(.*?)\s+(.*?)\s*$/,
47
+ key_vals: 6,
48
+ multiple_values: false,
49
+ )
50
+ @params = conf.params
51
+ end
52
+
53
+ def to_s
54
+ 'inetd.conf'
55
+ end
56
+ end
@@ -0,0 +1,127 @@
1
+ # encoding: utf-8
2
+ # author: Christoph Hartmann
3
+ # author: Dominik Richter
4
+
5
+ # Usage:
6
+ # describe interface('eth0') do
7
+ # it { should exist }
8
+ # it { should be_up }
9
+ # its(:speed) { should eq 1000 }
10
+ # end
11
+
12
+ require 'utils/convert'
13
+
14
+ class NetworkInterface < Inspec.resource(1)
15
+ name 'interface'
16
+
17
+ def initialize(iface)
18
+ @iface = iface
19
+
20
+ @interface_provider = nil
21
+ if inspec.os.linux?
22
+ @interface_provider = LinuxInterface.new(inspec)
23
+ elsif inspec.os.windows?
24
+ @interface_provider = WindowsInterface.new(inspec)
25
+ else
26
+ return skip_resource 'The `interface` resource is not supported on your OS yet.'
27
+ end
28
+ end
29
+
30
+ def exists?
31
+ !interface_info.nil? && !interface_info[:name].nil?
32
+ end
33
+
34
+ def up?
35
+ interface_info.nil? ? false : interface_info[:up]
36
+ end
37
+
38
+ # returns link speed in Mbits/sec
39
+ def speed
40
+ interface_info.nil? ? nil : interface_info[:speed]
41
+ end
42
+
43
+ def to_s
44
+ "Interface #{@iface}"
45
+ end
46
+
47
+ private
48
+
49
+ def interface_info
50
+ return @cache if defined?(@cache)
51
+ @cache = @interface_provider.interface_info(@iface) if !@interface_provider.nil?
52
+ end
53
+ end
54
+
55
+ class InterfaceInfo
56
+ include Converter
57
+ attr_reader :inspec
58
+ def initialize(inspec)
59
+ @inspec = inspec
60
+ end
61
+ end
62
+
63
+ class LinuxInterface < InterfaceInfo
64
+ def interface_info(iface)
65
+ # will return "[mtu]\n1500\n[type]\n1"
66
+ cmd = inspec.command("find /sys/class/net/#{iface}/ -type f -maxdepth 1 -exec sh -c 'echo \"[$(basename {})]\"; cat {} || echo -n' \\;")
67
+ return nil if cmd.exit_status.to_i != 0
68
+
69
+ # parse values, we only recieve values, therefore we threat them as keys
70
+ params = SimpleConfig.new(cmd.stdout.chomp).params
71
+
72
+ # abort if we got an empty result-set
73
+ return nil if params.empty?
74
+
75
+ # parse state
76
+ state = false
77
+ if params.key?('operstate')
78
+ operstate, _value = params['operstate'].first
79
+ state = operstate == 'up'
80
+ end
81
+
82
+ # parse speed
83
+ speed = nil
84
+ if params.key?('speed')
85
+ speed, _value = params['speed'].first
86
+ speed = convert_to_i(speed)
87
+ end
88
+
89
+ {
90
+ name: iface,
91
+ up: state,
92
+ speed: speed,
93
+ }
94
+ end
95
+ end
96
+
97
+ class WindowsInterface < InterfaceInfo
98
+ def interface_info(iface)
99
+ # gather all network interfaces
100
+ cmd = inspec.command('Get-NetAdapter | Select-Object -Property Name, InterfaceDescription, Status, State, MacAddress, LinkSpeed, ReceiveLinkSpeed, TransmitLinkSpeed, Virtual | ConvertTo-Json')
101
+
102
+ # filter network interface
103
+ begin
104
+ net_adapter = JSON.parse(cmd.stdout)
105
+ rescue JSON::ParserError => _e
106
+ return nil
107
+ end
108
+
109
+ # ensure we have an array of groups
110
+ net_adapter = [net_adapter] if !net_adapter.is_a?(Array)
111
+
112
+ # select the requested interface
113
+ adapters = net_adapter.each_with_object([]) do |adapter, adapter_collection|
114
+ # map object
115
+ info = {
116
+ name: adapter['Name'],
117
+ up: adapter['State'] == 2,
118
+ speed: adapter['ReceiveLinkSpeed'] / 1000,
119
+ }
120
+ adapter_collection.push(info) if info[:name].casecmp(iface) == 0
121
+ end
122
+
123
+ return nil if adapters.size == 0
124
+ warn "[Possible Error] detected multiple network interfaces with the name #{iface}" if adapters.size > 1
125
+ adapters[0]
126
+ end
127
+ end