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.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.rubocop.yml +65 -0
- data/.travis.yml +23 -0
- data/CHANGELOG.md +38 -0
- data/Gemfile +33 -0
- data/LICENSE +201 -0
- data/MAINTAINERS.md +28 -0
- data/MAINTAINERS.toml +42 -0
- data/README.md +257 -0
- data/Rakefile +47 -0
- data/bin/inspec +109 -0
- data/docs/ctl_inspec.rst +195 -0
- data/docs/dsl_inspec.rst +182 -0
- data/docs/readme.rst +100 -0
- data/docs/resources.rst +4319 -0
- data/docs/template.rst +51 -0
- data/examples/test-kitchen/.kitchen.yml +20 -0
- data/examples/test-kitchen/Berksfile +3 -0
- data/examples/test-kitchen/Gemfile +21 -0
- data/examples/test-kitchen/README.md +27 -0
- data/examples/test-kitchen/metadata.rb +7 -0
- data/examples/test-kitchen/recipes/default.rb +6 -0
- data/examples/test-kitchen/recipes/nginx.rb +30 -0
- data/examples/test-kitchen/test/integration/default/web_spec.rb +28 -0
- data/inspec.gemspec +30 -0
- data/lib/inspec.rb +20 -0
- data/lib/inspec/backend.rb +42 -0
- data/lib/inspec/dsl.rb +151 -0
- data/lib/inspec/log.rb +34 -0
- data/lib/inspec/metadata.rb +79 -0
- data/lib/inspec/plugins.rb +9 -0
- data/lib/inspec/plugins/resource.rb +62 -0
- data/lib/inspec/profile.rb +138 -0
- data/lib/inspec/profile_context.rb +170 -0
- data/lib/inspec/resource.rb +76 -0
- data/lib/inspec/rspec_json_formatter.rb +27 -0
- data/lib/inspec/rule.rb +170 -0
- data/lib/inspec/runner.rb +154 -0
- data/lib/inspec/shell.rb +66 -0
- data/lib/inspec/targets.rb +9 -0
- data/lib/inspec/targets/core.rb +27 -0
- data/lib/inspec/targets/dir.rb +67 -0
- data/lib/inspec/targets/file.rb +29 -0
- data/lib/inspec/targets/folder.rb +43 -0
- data/lib/inspec/targets/tar.rb +34 -0
- data/lib/inspec/targets/url.rb +39 -0
- data/lib/inspec/targets/zip.rb +47 -0
- data/lib/inspec/version.rb +7 -0
- data/lib/matchers/matchers.rb +221 -0
- data/lib/resources/apache.rb +29 -0
- data/lib/resources/apache_conf.rb +113 -0
- data/lib/resources/apt.rb +140 -0
- data/lib/resources/audit_policy.rb +63 -0
- data/lib/resources/auditd_conf.rb +56 -0
- data/lib/resources/auditd_rules.rb +53 -0
- data/lib/resources/bond.rb +65 -0
- data/lib/resources/bridge.rb +114 -0
- data/lib/resources/command.rb +57 -0
- data/lib/resources/csv.rb +32 -0
- data/lib/resources/directory.rb +15 -0
- data/lib/resources/etc_group.rb +150 -0
- data/lib/resources/file.rb +110 -0
- data/lib/resources/gem.rb +46 -0
- data/lib/resources/group.rb +132 -0
- data/lib/resources/host.rb +143 -0
- data/lib/resources/inetd_conf.rb +56 -0
- data/lib/resources/interface.rb +127 -0
- data/lib/resources/iptables.rb +65 -0
- data/lib/resources/json.rb +64 -0
- data/lib/resources/kernel_module.rb +40 -0
- data/lib/resources/kernel_parameter.rb +55 -0
- data/lib/resources/limits_conf.rb +55 -0
- data/lib/resources/login_def.rb +60 -0
- data/lib/resources/mysql.rb +81 -0
- data/lib/resources/mysql_conf.rb +116 -0
- data/lib/resources/mysql_session.rb +52 -0
- data/lib/resources/npm.rb +44 -0
- data/lib/resources/ntp_conf.rb +58 -0
- data/lib/resources/oneget.rb +63 -0
- data/lib/resources/os.rb +22 -0
- data/lib/resources/os_env.rb +34 -0
- data/lib/resources/package.rb +169 -0
- data/lib/resources/parse_config.rb +75 -0
- data/lib/resources/passwd.rb +93 -0
- data/lib/resources/pip.rb +75 -0
- data/lib/resources/port.rb +296 -0
- data/lib/resources/postgres.rb +37 -0
- data/lib/resources/postgres_conf.rb +87 -0
- data/lib/resources/postgres_session.rb +59 -0
- data/lib/resources/processes.rb +57 -0
- data/lib/resources/registry_key.rb +54 -0
- data/lib/resources/script.rb +34 -0
- data/lib/resources/security_policy.rb +73 -0
- data/lib/resources/service.rb +379 -0
- data/lib/resources/ssh_conf.rb +75 -0
- data/lib/resources/user.rb +374 -0
- data/lib/resources/windows_feature.rb +77 -0
- data/lib/resources/yaml.rb +23 -0
- data/lib/resources/yum.rb +154 -0
- data/lib/utils/convert.rb +12 -0
- data/lib/utils/detect.rb +15 -0
- data/lib/utils/find_files.rb +36 -0
- data/lib/utils/hash.rb +13 -0
- data/lib/utils/modulator.rb +12 -0
- data/lib/utils/parser.rb +61 -0
- data/lib/utils/simpleconfig.rb +115 -0
- data/tasks/maintainers.rb +213 -0
- data/test/docker_run.rb +156 -0
- data/test/docker_test.rb +51 -0
- data/test/helper.rb +200 -0
- data/test/integration/.kitchen.yml +42 -0
- data/test/integration/Berksfile +4 -0
- data/test/integration/cookbooks/os_prepare/metadata.rb +8 -0
- data/test/integration/cookbooks/os_prepare/recipes/apt.rb +20 -0
- data/test/integration/cookbooks/os_prepare/recipes/default.rb +9 -0
- data/test/integration/cookbooks/os_prepare/recipes/file.rb +21 -0
- data/test/integration/cookbooks/os_prepare/recipes/package.rb +26 -0
- data/test/integration/default/_debug_spec.rb +1 -0
- data/test/integration/default/apt_spec.rb +42 -0
- data/test/integration/default/file_spec.rb +109 -0
- data/test/integration/default/group_spec.rb +32 -0
- data/test/integration/default/kernel_module_spec.rb +17 -0
- data/test/integration/default/kernel_parameter_spec.rb +56 -0
- data/test/integration/default/package_spec.rb +11 -0
- data/test/integration/default/service_spec.rb +28 -0
- data/test/integration/default/user_spec.rb +44 -0
- data/test/resource/command_test.rb +33 -0
- data/test/resource/dsl_test.rb +45 -0
- data/test/resource/file_test.rb +130 -0
- data/test/resource/ssh_config.rb +9 -0
- data/test/resource/sshd_config.rb +9 -0
- data/test/test-extra.yaml +11 -0
- data/test/test.yaml +11 -0
- data/test/unit/mock/cmd/Get-NetAdapter +24 -0
- data/test/unit/mock/cmd/GetUserAccount +33 -0
- data/test/unit/mock/cmd/GetWin32Group +23 -0
- data/test/unit/mock/cmd/PATH +1 -0
- data/test/unit/mock/cmd/Resolve-DnsName +26 -0
- data/test/unit/mock/cmd/Test-NetConnection +4 -0
- data/test/unit/mock/cmd/auditctl +7 -0
- data/test/unit/mock/cmd/auditpol +2 -0
- data/test/unit/mock/cmd/brew-info-jq +1 -0
- data/test/unit/mock/cmd/chage-l-root +7 -0
- data/test/unit/mock/cmd/dpkg-s-curl +21 -0
- data/test/unit/mock/cmd/dscl +5 -0
- data/test/unit/mock/cmd/etc-apt +7 -0
- data/test/unit/mock/cmd/find-etc-rc-d-name-S +12 -0
- data/test/unit/mock/cmd/find-net-interface +9 -0
- data/test/unit/mock/cmd/gem-list-local-a-q-rubocop +1 -0
- data/test/unit/mock/cmd/get-net-tcpconnection +24 -0
- data/test/unit/mock/cmd/get-netadapter-binding-bridge +4 -0
- data/test/unit/mock/cmd/get-package-firefox +30 -0
- data/test/unit/mock/cmd/get-package-ruby +18 -0
- data/test/unit/mock/cmd/get-service-dhcp +10 -0
- data/test/unit/mock/cmd/get-windows-feature +7 -0
- data/test/unit/mock/cmd/getent-hosts-example.com +1 -0
- data/test/unit/mock/cmd/getent-passwd-root +1 -0
- data/test/unit/mock/cmd/id-chartmann +1 -0
- data/test/unit/mock/cmd/id-root +1 -0
- data/test/unit/mock/cmd/initctl-show-config-ssh +3 -0
- data/test/unit/mock/cmd/initctl-status-ssh +1 -0
- data/test/unit/mock/cmd/iptables-s +6 -0
- data/test/unit/mock/cmd/launchctl-list +3 -0
- data/test/unit/mock/cmd/ls-1-etc-init.d +2 -0
- data/test/unit/mock/cmd/ls-sys-class-net-br +2 -0
- data/test/unit/mock/cmd/lsmod +2 -0
- data/test/unit/mock/cmd/lsof-np-itcp +4 -0
- data/test/unit/mock/cmd/netstat-tulpen +5 -0
- data/test/unit/mock/cmd/npm-ls-g--json-bower +9 -0
- data/test/unit/mock/cmd/pacman-qi-curl +21 -0
- data/test/unit/mock/cmd/ping-example.com +6 -0
- data/test/unit/mock/cmd/pip-show-jinja2 +11 -0
- data/test/unit/mock/cmd/ps-aux +3 -0
- data/test/unit/mock/cmd/pw-usershow-root-7 +1 -0
- data/test/unit/mock/cmd/reg_schedule +1 -0
- data/test/unit/mock/cmd/rpm-qia-curl +24 -0
- data/test/unit/mock/cmd/sbin_sysctl +1 -0
- data/test/unit/mock/cmd/secedit-export +7 -0
- data/test/unit/mock/cmd/service-e +2 -0
- data/test/unit/mock/cmd/service-sendmail-onestatus +3 -0
- data/test/unit/mock/cmd/service-sshd-status +1 -0
- data/test/unit/mock/cmd/sockstat +5 -0
- data/test/unit/mock/cmd/success +0 -0
- data/test/unit/mock/cmd/systemctl-show-all-sshd +6 -0
- data/test/unit/mock/cmd/win32_product +8 -0
- data/test/unit/mock/cmd/yum-repolist-all +52 -0
- data/test/unit/mock/files/auditd.conf +4 -0
- data/test/unit/mock/files/bond0 +37 -0
- data/test/unit/mock/files/etcgroup +3 -0
- data/test/unit/mock/files/example.csv +6 -0
- data/test/unit/mock/files/inetd.conf +2 -0
- data/test/unit/mock/files/kitchen.yml +7 -0
- data/test/unit/mock/files/limits.conf +5 -0
- data/test/unit/mock/files/login.defs +5 -0
- data/test/unit/mock/files/mysql.conf +8 -0
- data/test/unit/mock/files/mysql2.conf +2 -0
- data/test/unit/mock/files/ntp.conf +5 -0
- data/test/unit/mock/files/passwd +2 -0
- data/test/unit/mock/files/policyfile.lock.json +12 -0
- data/test/unit/mock/files/ssh_config +5 -0
- data/test/unit/mock/files/sshd_config +7 -0
- data/test/unit/mock/profiles/empty/metadata.rb +0 -0
- data/test/unit/mock/profiles/metadata/metadata.rb +1 -0
- data/test/unit/profile_context_test.rb +140 -0
- data/test/unit/profile_test.rb +49 -0
- data/test/unit/resources/apt_test.rb +46 -0
- data/test/unit/resources/audit_policy_test.rb +13 -0
- data/test/unit/resources/auditd_conf_test.rb +15 -0
- data/test/unit/resources/auditd_rules_test.rb +21 -0
- data/test/unit/resources/bond_test.rb +24 -0
- data/test/unit/resources/bridge_test.rb +56 -0
- data/test/unit/resources/csv_test.rb +35 -0
- data/test/unit/resources/etc_group_test.rb +37 -0
- data/test/unit/resources/gem_test.rb +20 -0
- data/test/unit/resources/group_test.rb +96 -0
- data/test/unit/resources/host_test.rb +38 -0
- data/test/unit/resources/inetd_conf_test.rb +15 -0
- data/test/unit/resources/interface_test.rb +54 -0
- data/test/unit/resources/iptables_test.rb +30 -0
- data/test/unit/resources/json_test.rb +36 -0
- data/test/unit/resources/kernel_module_test.rb +23 -0
- data/test/unit/resources/kernel_parameter_test.rb +13 -0
- data/test/unit/resources/limits_conf_test.rb +14 -0
- data/test/unit/resources/login_def_test.rb +16 -0
- data/test/unit/resources/mysql_conf_test.rb +14 -0
- data/test/unit/resources/npm_test.rb +20 -0
- data/test/unit/resources/ntp_conf_test.rb +16 -0
- data/test/unit/resources/oneget_test.rb +45 -0
- data/test/unit/resources/os_env_test.rb +13 -0
- data/test/unit/resources/package_test.rb +51 -0
- data/test/unit/resources/passwd_test.rb +24 -0
- data/test/unit/resources/pip_test.rb +15 -0
- data/test/unit/resources/port_test.rb +46 -0
- data/test/unit/resources/processes_test.rb +32 -0
- data/test/unit/resources/registry_key_test.rb +19 -0
- data/test/unit/resources/script_test.rb +19 -0
- data/test/unit/resources/security_policy_test.rb +16 -0
- data/test/unit/resources/service_test.rb +116 -0
- data/test/unit/resources/ssh_conf_test.rb +33 -0
- data/test/unit/resources/user_test.rb +93 -0
- data/test/unit/resources/windows_feature.rb +17 -0
- data/test/unit/resources/yaml_test.rb +34 -0
- data/test/unit/resources/yum_test.rb +68 -0
- data/test/unit/simpleconfig_test.rb +80 -0
- data/test/unit/utils/content_parser_test.rb +30 -0
- 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,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
|