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,23 @@
1
+ # encoding: utf-8
2
+ # author: Christoph Hartmann
3
+ # author: Dominik Richter
4
+
5
+ require 'yaml'
6
+
7
+ # Parses a yaml document
8
+ # Usage:
9
+ # describe yaml('.kitchen.yaml') do
10
+ # its('driver.name') { should eq('vagrant') }
11
+ # end
12
+ class YamlConfig < JsonConfig
13
+ name 'yaml'
14
+
15
+ # override file load and parse hash from yaml
16
+ def parse(content)
17
+ YAML.load(content)
18
+ end
19
+
20
+ def to_s
21
+ "YAML #{@path}"
22
+ end
23
+ end
@@ -0,0 +1,154 @@
1
+ # encoding: utf-8
2
+ # author: Christoph Hartmann
3
+ # author: Dominik Richter
4
+
5
+ require 'resources/file'
6
+
7
+ # Usage:
8
+ # describe yum do
9
+ # its('repos') { should exist }
10
+ # end
11
+ #
12
+ # describe yum do
13
+ # its('repos') { should include 'base/7/x86_64' }
14
+ # its('epel') { should exist }
15
+ # its('epel') { should be_enabled }
16
+ # end
17
+ #
18
+ # Filter for a specific repo by name
19
+ # - use full identifier e.g. 'updates/7/x86_64'
20
+ # - use short identifier e.g. 'updates'
21
+ #
22
+ # describe yum.repo('epel') do
23
+ # it { should exist }
24
+ # it { should be_enabled }
25
+ # end
26
+ #
27
+ # deprecated:
28
+ # describe yumrepo('epel') do
29
+ # it { should exist }
30
+ # it { should be_enabled }
31
+ # end
32
+
33
+ class Yum < Inspec.resource(1)
34
+ name 'yum'
35
+
36
+ # returns all repositories
37
+ # works as following:
38
+ # search for Repo-id
39
+ # parse data in hashmap
40
+ # store data in object
41
+ # until \n
42
+ def repositories
43
+ return @cache if defined?(@cache)
44
+ # parse the repository data from yum
45
+ # we cannot use -C, because this is not reliable and may lead to errors
46
+ @command_result = inspec.command('yum -v repolist all')
47
+ @content = @command_result.stdout
48
+ @cache = []
49
+ repo = {}
50
+ in_repo = false
51
+ @content.each_line do |line|
52
+ # detect repo start
53
+ in_repo = true if line.match(/^\s*Repo-id\s*:\s*(.*)\b/)
54
+ # detect repo end
55
+ if line == "\n" && in_repo
56
+ in_repo = false
57
+ @cache.push(repo)
58
+ repo = {}
59
+ end
60
+ # parse repo content
61
+ if in_repo == true
62
+ val = /^\s*([^:]*?)\s*:\s*(.*?)\s*$/.match(line)
63
+ repo[repo_key(strip(val[1]))] = strip(val[2])
64
+ end
65
+ end
66
+ @cache
67
+ end
68
+
69
+ def repos
70
+ repositories.map { |repo| repo['id'] }
71
+ end
72
+
73
+ def repo(repo)
74
+ YumRepo.new(self, repo)
75
+ end
76
+
77
+ # alias for yum.repo('reponame')
78
+ def method_missing(name)
79
+ repo(name.to_s) if !name.nil?
80
+ end
81
+
82
+ def to_s
83
+ 'Yum Repository'
84
+ end
85
+
86
+ private
87
+
88
+ # Removes lefthand and righthand whitespace
89
+ def strip(value)
90
+ value.lstrip.rstrip if !value.nil?
91
+ end
92
+
93
+ # Optimize the key value
94
+ def repo_key(key)
95
+ return key if key.nil?
96
+ key.gsub('Repo-', '').downcase
97
+ end
98
+ end
99
+
100
+ class YumRepo
101
+ def initialize(yum, reponame)
102
+ @yum = yum
103
+ @reponame = reponame
104
+ end
105
+
106
+ # extracts the shortname from a repo id
107
+ # e.g. extras/7/x86_64 -> extras
108
+ def shortname(id)
109
+ val = %r{^\s*([^/]*?)/(.*?)\s*$}.match(id)
110
+ val.nil? ? nil : val[1]
111
+ end
112
+
113
+ def info
114
+ return @cache if defined?(@cache)
115
+ selection = @yum.repositories.select { |e| e['id'] == @reponame || shortname(e['id']) == @reponame }
116
+ @cache = selection[0] if !selection.nil? && selection.length == 1
117
+ @cache
118
+ end
119
+
120
+ def exist?
121
+ !info.nil?
122
+ end
123
+
124
+ def enabled?
125
+ repo = info
126
+ return false if repo.nil?
127
+ info['status'] == 'enabled'
128
+ end
129
+ end
130
+
131
+ # for compatability with serverspec
132
+ # this is deprecated syntax and will be removed in future versions
133
+ class YumRepoLegacy < Yum
134
+ name 'yumrepo'
135
+
136
+ def initialize(name)
137
+ super()
138
+ @repository = repo(name)
139
+ end
140
+
141
+ def exists?
142
+ deprecated
143
+ @repository.exist?
144
+ end
145
+
146
+ def enabled?
147
+ deprecated
148
+ @repository.enabled?
149
+ end
150
+
151
+ def deprecated
152
+ warn '[DEPRECATION] `yumrepo(reponame)` is deprecated. Please use `yum.repo(reponame)` instead.'
153
+ end
154
+ end
@@ -0,0 +1,12 @@
1
+ # encoding: utf-8
2
+ # author: Dominik Richter
3
+ # author: Christoph Hartmann
4
+
5
+ module Converter
6
+ # convert the value to an integer if we have numbers only
7
+ # otherwise we return the string
8
+ def convert_to_i(val)
9
+ val = val.to_i if val.match(/^\d+$/)
10
+ val
11
+ end
12
+ end
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+ # Copyright 2015, Vulcano Security GmbH
3
+ # author: Christoph Hartmann
4
+ # author: Dominik Richter
5
+ # Tiny test file to return OS info of the tested node
6
+
7
+ # print OS detection infos
8
+ conf = {
9
+ name: os[:name],
10
+ family: os[:family],
11
+ release: os[:release],
12
+ arch: os[:arch],
13
+ }
14
+ puts JSON.dump(conf)
15
+ exit 0
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+ # copyright: 2015, Vulcano Security GmbH
3
+ # license: All rights reserved
4
+ # author: Dominik Richter
5
+ # author: Christoph Hartmann
6
+
7
+ module FindFiles
8
+ TYPES = {
9
+ block: 'b',
10
+ character: 'c',
11
+ directory: 'd',
12
+ pipe: 'p',
13
+ file: 'f',
14
+ link: 'l',
15
+ socket: 's',
16
+ door: 'D',
17
+ }
18
+
19
+ def find_files(path, opts = {})
20
+ depth = opts[:depth]
21
+ type = TYPES[opts[:type].to_sym]
22
+
23
+ cmd = "find #{path}"
24
+ cmd += " -maxdepth #{depth.to_i}" if depth.to_i > 0
25
+ cmd += " -type #{type}" unless type.nil?
26
+
27
+ result = inspec.run_command(cmd)
28
+ exit_status = result.exit_status
29
+
30
+ return [nil, exit_status] unless exit_status == 0
31
+ files = result.stdout.split("\n")
32
+ .map(&:strip)
33
+ .find_all { |x| !x.empty? }
34
+ [files, exit_status]
35
+ end
36
+ end
data/lib/utils/hash.rb ADDED
@@ -0,0 +1,13 @@
1
+ # encoding: utf-8
2
+ # Inspired by: http://stackoverflow.com/a/9381776
3
+ # author: Dominik Richter
4
+ # author: Christoph Hartmann
5
+
6
+ class ::Hash
7
+ def deep_merge(second)
8
+ merger = proc { |_key, v1, v2|
9
+ v1.is_a?(Hash) && v2.is_a?(Hash) ? v1.merge(v2, &merger) : v2
10
+ }
11
+ merge(second, &merger)
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ # encoding: utf-8
2
+ # author: Dominik Richter
3
+ # author: Christoph Hartmann
4
+ module Modulator
5
+ def modules
6
+ @modules ||= {}
7
+ end
8
+
9
+ def add_module(name, handler)
10
+ modules[name] = handler
11
+ end
12
+ end
@@ -0,0 +1,61 @@
1
+ # encoding: utf-8
2
+ # author: Christoph Hartmann
3
+ # author: Dominik Richter
4
+
5
+ module ContentParser
6
+ # Parse /etc/passwd files.
7
+ #
8
+ # @param [String] content the raw content of /etc/passwd
9
+ # @return [Array] Collection of passwd entries
10
+ def parse_passwd(content)
11
+ content.to_s.split("\n").map do |line|
12
+ parse_passwd_line(line)
13
+ end
14
+ end
15
+
16
+ # Parse a line of /etc/passwd
17
+ #
18
+ # @param [String] line a line of /etc/passwd
19
+ # @return [Hash] Map of entries in this line
20
+ def parse_passwd_line(line)
21
+ x = line.split(':')
22
+ {
23
+ 'name' => x.at(0),
24
+ 'password' => x.at(1),
25
+ 'uid' => x.at(2),
26
+ 'gid' => x.at(3),
27
+ 'desc' => x.at(4),
28
+ 'home' => x.at(5),
29
+ 'shell' => x.at(6),
30
+ }
31
+ end
32
+
33
+ # Parse a line with a command. For example: `a = b # comment`.
34
+ # Retrieves the actual content.
35
+ #
36
+ # @param [String] raw the content lines you want to be parsed
37
+ # @param [Hash] opts optional configuration
38
+ # @return [Array] contains the actual line and the position of the line end
39
+ def parse_comment_line(raw, opts)
40
+ idx_nl = raw.index("\n")
41
+ idx_comment = raw.index(opts[:comment_char])
42
+ idx_nl = raw.length if idx_nl.nil?
43
+ idx_comment = idx_nl + 1 if idx_comment.nil?
44
+ line = ''
45
+
46
+ # is a comment inside this line
47
+ if idx_comment < idx_nl && idx_comment != 0
48
+ line = raw[0..(idx_comment - 1)]
49
+ # in case we don't allow comments at the end
50
+ # of an assignment/statement, ignore it and fall
51
+ # back to treating this as a regular line
52
+ if opts[:standalone_comments] && !is_empty_line(line)
53
+ line = raw[0..(idx_nl - 1)]
54
+ end
55
+ # if there is no comment in this line
56
+ elsif idx_comment > idx_nl && idx_nl != 0
57
+ line = raw[0..(idx_nl - 1)]
58
+ end
59
+ [line, idx_nl]
60
+ end
61
+ end
@@ -0,0 +1,115 @@
1
+ # encoding: utf-8
2
+ # copyright: 2015, Dominik Richter
3
+ # license: All rights reserved
4
+ # author: Dominik Richter
5
+ # author: Christoph Hartmann
6
+
7
+ require 'utils/parser'
8
+
9
+ class SimpleConfig
10
+ include ContentParser
11
+
12
+ attr_reader :params, :groups
13
+ def initialize(raw_data, opts = {})
14
+ parse(raw_data, opts)
15
+ end
16
+
17
+ # Parse some data
18
+ # quotes: quoting characters, which are parsed, so everything inside
19
+ # it will be part of a string
20
+ # multiline: allow quoted text to span multiple lines
21
+ # comment_char: char which identifies comments
22
+ # standalone_comments: comments must appear alone in a line; if set to true,
23
+ # no comments can be added to the end of an assignment/statement line
24
+ def parse(raw_data, opts = nil)
25
+ @params = {}
26
+ @groups = []
27
+ @vals = @params
28
+ options = default_options.merge(opts || {})
29
+ return if raw_data.nil?
30
+
31
+ # prepare raw data if required
32
+ if !options[:line_separator].nil?
33
+ raw_data = raw_data.tr(options[:line_separator], "\n")
34
+ end
35
+ rest = raw_data
36
+ rest = parse_rest(rest, options) while rest.length > 0
37
+ end
38
+
39
+ private
40
+
41
+ def parse_values(match, values)
42
+ start_idx = 2
43
+ i = 0
44
+ count = values - 1
45
+ return match[start_idx] if (values == 1)
46
+
47
+ # iterate over expected parameters
48
+ values = []
49
+ loop do
50
+ values.push(match[start_idx + i])
51
+ i += 1
52
+ break if i > count
53
+ end
54
+ values
55
+ end
56
+
57
+ def parse_params_line(line, opts)
58
+ # now line contains what we are interested in parsing
59
+ # check if it is an assignment
60
+ m = opts[:assignment_re].match(line)
61
+ return nil if m.nil?
62
+
63
+ if opts[:multiple_values]
64
+ @vals[m[1]] ||= []
65
+ @vals[m[1]].push(parse_values(m, opts[:key_vals]))
66
+ else
67
+ @vals[m[1]] = parse_values(m, opts[:key_vals])
68
+ end
69
+ end
70
+
71
+ def parse_group_line(line, opts)
72
+ return nil if opts[:group_re].nil?
73
+ m = opts[:group_re].match(line)
74
+ return nil if m.nil?
75
+ @groups.push(m[1])
76
+ @vals = @params[m[1]] = {}
77
+ end
78
+
79
+ def parse_implicit_assignment_line(line, opts)
80
+ return nil if is_empty_line(line)
81
+ if opts[:multiple_values]
82
+ @vals[line.strip] ||= []
83
+ else
84
+ @vals[line.strip] = ''
85
+ end
86
+ end
87
+
88
+ def parse_rest(rest, opts)
89
+ line, idx_nl = parse_comment_line(rest, opts)
90
+ parse_params_line(line, opts) or
91
+ parse_group_line(line, opts) or
92
+ parse_implicit_assignment_line(line, opts)
93
+
94
+ # return whatever is left
95
+ rest[(idx_nl + 1)..-1] || ''
96
+ end
97
+
98
+ def is_empty_line(l)
99
+ l =~ /^\s*$/
100
+ end
101
+
102
+ def default_options
103
+ {
104
+ quotes: '',
105
+ multiline: false,
106
+ comment_char: '#',
107
+ line_separator: nil, # uses this char to seperate lines before parsing
108
+ assignment_re: /^\s*([^=]*?)\s*=\s*(.*?)\s*$/,
109
+ group_re: /\[([^\]]+)\]\s*$/,
110
+ key_vals: 1, # default for key=value, may require for 'key val1 val2 val3'
111
+ standalone_comments: false,
112
+ multiple_values: false,
113
+ }
114
+ end
115
+ end