inspec 1.51.0 → 1.51.6

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 (111) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -15
  3. data/README.md +1 -1
  4. data/docs/glossary.md +99 -0
  5. data/docs/resources/aide_conf.md.erb +16 -9
  6. data/docs/resources/apache.md.erb +66 -0
  7. data/docs/resources/apache_conf.md.erb +11 -5
  8. data/docs/resources/apt.md.erb +1 -1
  9. data/docs/resources/audit_policy.md.erb +1 -1
  10. data/docs/resources/auditd_conf.md.erb +12 -9
  11. data/docs/resources/bash.md.erb +24 -12
  12. data/docs/resources/bond.md.erb +26 -24
  13. data/docs/resources/bridge.md.erb +18 -11
  14. data/docs/resources/bsd_service.md.erb +11 -2
  15. data/docs/resources/command.md.erb +30 -29
  16. data/docs/resources/cpan.md.erb +33 -17
  17. data/docs/resources/cran.md.erb +26 -17
  18. data/docs/resources/crontab.md.erb +18 -1
  19. data/docs/resources/csv.md.erb +13 -7
  20. data/docs/resources/{dh_params.md → dh_params.md.erb} +30 -6
  21. data/docs/resources/directory.md.erb +9 -4
  22. data/docs/resources/docker.md.erb +1 -1
  23. data/docs/resources/docker_container.md.erb +32 -26
  24. data/docs/resources/docker_image.md.erb +29 -26
  25. data/docs/resources/docker_service.md.erb +37 -31
  26. data/docs/resources/elasticsearch.md.erb +18 -32
  27. data/docs/resources/etc_fstab.md.erb +19 -15
  28. data/docs/resources/etc_group.md.erb +13 -39
  29. data/docs/resources/etc_hosts.md.erb +12 -5
  30. data/docs/resources/etc_hosts_allow.md.erb +9 -4
  31. data/docs/resources/etc_hosts_deny.md.erb +12 -7
  32. data/docs/resources/file.md.erb +139 -134
  33. data/docs/resources/filesystem.md.erb +5 -4
  34. data/docs/resources/firewalld.md.erb +1 -1
  35. data/docs/resources/gem.md.erb +2 -2
  36. data/docs/resources/group.md.erb +1 -1
  37. data/docs/resources/host.md.erb +1 -1
  38. data/docs/resources/iis_app.md.erb +1 -1
  39. data/docs/resources/iis_site.md.erb +1 -1
  40. data/docs/resources/interface.md.erb +1 -1
  41. data/docs/resources/iptables.md.erb +1 -1
  42. data/docs/resources/json.md.erb +1 -1
  43. data/docs/resources/kernel_module.md.erb +1 -1
  44. data/docs/resources/kernel_parameter.md.erb +1 -1
  45. data/docs/resources/launchd_service.md.erb +1 -1
  46. data/docs/resources/limits_conf.md.erb +1 -1
  47. data/docs/resources/login_def.md.erb +1 -1
  48. data/docs/resources/mount.md.erb +1 -1
  49. data/docs/resources/mysql_conf.md.erb +1 -1
  50. data/docs/resources/nginx_conf.md.erb +1 -1
  51. data/docs/resources/npm.md.erb +1 -1
  52. data/docs/resources/oneget.md.erb +1 -1
  53. data/docs/resources/os.md.erb +1 -1
  54. data/docs/resources/os_env.md.erb +2 -2
  55. data/docs/resources/package.md.erb +1 -1
  56. data/docs/resources/packages.md.erb +66 -0
  57. data/docs/resources/parse_config.md.erb +1 -1
  58. data/docs/resources/parse_config_file.md.erb +1 -1
  59. data/docs/resources/passwd.md.erb +1 -1
  60. data/docs/resources/pip.md.erb +1 -1
  61. data/docs/resources/port.md.erb +1 -1
  62. data/docs/resources/postgres_conf.md.erb +1 -1
  63. data/docs/resources/postgres_session.md.erb +1 -1
  64. data/docs/resources/powershell.md.erb +2 -2
  65. data/docs/resources/processes.md.erb +1 -1
  66. data/docs/resources/registry_key.md.erb +1 -1
  67. data/docs/resources/runit_service.md.erb +1 -1
  68. data/docs/resources/security_policy.md.erb +1 -1
  69. data/docs/resources/service.md.erb +1 -1
  70. data/docs/resources/shadow.md.erb +1 -1
  71. data/docs/resources/ssh_config.md.erb +1 -1
  72. data/docs/resources/sshd_config.md.erb +1 -1
  73. data/docs/resources/ssl.md.erb +1 -1
  74. data/docs/resources/sys_info.md.erb +1 -1
  75. data/docs/resources/systemd_service.md.erb +1 -1
  76. data/docs/resources/sysv_service.md.erb +1 -1
  77. data/docs/resources/upstart_service.md.erb +1 -1
  78. data/docs/resources/user.md.erb +1 -1
  79. data/docs/resources/users.md.erb +1 -1
  80. data/docs/resources/windows_feature.md.erb +1 -1
  81. data/docs/resources/windows_hotfix.md.erb +1 -1
  82. data/docs/resources/xinetd_conf.md.erb +1 -1
  83. data/docs/resources/xml.md.erb +1 -1
  84. data/docs/resources/yaml.md.erb +1 -1
  85. data/docs/resources/yum.md.erb +1 -1
  86. data/lib/inspec.rb +2 -1
  87. data/lib/inspec/base_cli.rb +98 -18
  88. data/lib/inspec/cli.rb +33 -21
  89. data/lib/inspec/formatters.rb +3 -0
  90. data/lib/inspec/formatters/base.rb +208 -0
  91. data/lib/inspec/formatters/json_rspec.rb +20 -0
  92. data/lib/inspec/formatters/show_progress.rb +12 -0
  93. data/lib/inspec/objects.rb +1 -0
  94. data/lib/inspec/objects/describe.rb +92 -0
  95. data/lib/inspec/reporters.rb +33 -0
  96. data/lib/inspec/reporters/base.rb +23 -0
  97. data/lib/inspec/reporters/cli.rb +395 -0
  98. data/lib/inspec/reporters/json.rb +132 -0
  99. data/lib/inspec/reporters/json_min.rb +44 -0
  100. data/lib/inspec/reporters/junit.rb +77 -0
  101. data/lib/inspec/runner.rb +14 -1
  102. data/lib/inspec/runner_rspec.rb +34 -14
  103. data/lib/inspec/schema.rb +1 -0
  104. data/lib/inspec/shell.rb +0 -1
  105. data/lib/inspec/version.rb +1 -1
  106. data/lib/resources/apache.rb +20 -0
  107. data/lib/resources/apache_conf.rb +33 -8
  108. data/lib/resources/audit_policy.rb +1 -1
  109. data/lib/resources/packages.rb +4 -3
  110. metadata +17 -4
  111. data/lib/inspec/rspec_json_formatter.rb +0 -940
@@ -0,0 +1,132 @@
1
+ # encoding: utf-8
2
+
3
+ require 'json'
4
+
5
+ module Inspec::Reporters
6
+ class Json < Base
7
+ def render
8
+ report = {
9
+ platform: platform,
10
+ profiles: profiles,
11
+ statistics: { duration: run_data[:statistics][:duration] },
12
+ version: run_data[:version],
13
+ controls: controls,
14
+ other_checks: run_data[:other_checks],
15
+ }
16
+
17
+ output(report.to_json)
18
+ end
19
+
20
+ private
21
+
22
+ def platform
23
+ {
24
+ name: run_data[:platform][:name],
25
+ release: run_data[:platform][:release],
26
+ }
27
+ end
28
+
29
+ def controls
30
+ controls = []
31
+ return controls if run_data[:controls].nil?
32
+
33
+ run_data[:controls].each do |c|
34
+ control = {
35
+ status: c[:status],
36
+ start_time: c[:start_time],
37
+ run_time: c[:run_time],
38
+ code_desc: c[:code_desc],
39
+ }
40
+ control[:resource] = c[:resource] if c[:resource]
41
+ control[:skip_message] = c[:skip_message] if c[:skip_message]
42
+ control[:exception] = c[:exception] if c[:exception]
43
+ control[:backtrace] = c[:backtrace] if c[:backtrace]
44
+
45
+ controls << control
46
+ end
47
+ controls
48
+ end
49
+
50
+ def profile_results(control)
51
+ results = []
52
+ return results if control[:results].nil?
53
+
54
+ control[:results].each do |r|
55
+ result = {
56
+ status: r[:status],
57
+ code_desc: r[:code_desc],
58
+ run_time: r[:run_time],
59
+ start_time: r[:start_time],
60
+ }
61
+ result[:resource] = r[:resource] if r[:resource]
62
+ result[:skip_message] = r[:skip_message] if r[:skip_message]
63
+
64
+ results << result
65
+ end
66
+ results
67
+ end
68
+
69
+ def profile_controls(profile)
70
+ controls = []
71
+ return controls if profile[:controls].nil?
72
+
73
+ profile[:controls].each do |c|
74
+ control = {
75
+ id: c[:id],
76
+ title: c[:title],
77
+ desc: c[:desc],
78
+ impact: c[:impact],
79
+ refs: c[:refs],
80
+ tags: c[:tags],
81
+ code: c[:code],
82
+ source_location: {
83
+ line: c[:source_location][:line],
84
+ ref: c[:source_location][:ref],
85
+ },
86
+ results: profile_results(c),
87
+ }
88
+ controls << control
89
+ end
90
+ controls
91
+ end
92
+
93
+ def profile_groups(profile)
94
+ groups = []
95
+ return groups if profile[:groups].nil?
96
+
97
+ profile[:groups].each do |g|
98
+ group = {
99
+ id: g[:id],
100
+ controls: g[:controls],
101
+ }
102
+ group[:title] = g[:title] if g[:title]
103
+
104
+ groups << group
105
+ end
106
+ groups
107
+ end
108
+
109
+ def profiles
110
+ profiles = []
111
+ run_data[:profiles].each do |p|
112
+ profile = {
113
+ name: p[:name],
114
+ version: p[:version],
115
+ sha256: p[:sha256],
116
+ title: p[:title],
117
+ maintainer: p[:maintainer],
118
+ summary: p[:summary],
119
+ license: p[:license],
120
+ copyright: p[:copyright],
121
+ copyright_email: p[:copyright_email],
122
+ supports: p[:supports],
123
+ attributes: p[:attributes],
124
+ groups: profile_groups(p),
125
+ controls: profile_controls(p),
126
+ }
127
+ profiles << profile.reject { |_k, v| v.nil? }
128
+ end
129
+ profiles
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,44 @@
1
+ # encoding: utf-8
2
+
3
+ require 'json'
4
+
5
+ module Inspec::Reporters
6
+ class JsonMin < Base
7
+ def render # rubocop:disable Metrics/AbcSize
8
+ report = {
9
+ controls: [],
10
+ statistics: { duration: run_data[:statistics][:duration] },
11
+ version: run_data[:version],
12
+ }
13
+
14
+ # collect all test results and add them to the report
15
+ run_data[:profiles].each do |profile|
16
+ profile_id = profile[:name]
17
+ next unless profile[:controls]
18
+ profile[:controls].each do |control|
19
+ control_id = control[:id]
20
+ next unless control[:results]
21
+ control[:results].each do |result|
22
+ result_for_report = {
23
+ id: control_id,
24
+ profile_id: profile_id,
25
+ profile_sha256: profile[:sha256],
26
+ status: result[:status],
27
+ code_desc: result[:code_desc],
28
+ }
29
+
30
+ result_for_report[:skip_message] = result[:skip_message] if result.key?(:skip_message)
31
+ result_for_report[:resource] = result[:resource] if result.key?(:resource)
32
+ result_for_report[:message] = result[:message] if result.key?(:message)
33
+ result_for_report[:exception] = result[:exception] if result.key?(:exception)
34
+ result_for_report[:backtrace] = result[:backtrace] if result.key?(:backtrace)
35
+
36
+ report[:controls] << result_for_report
37
+ end
38
+ end
39
+ end
40
+
41
+ output(report.to_json)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,77 @@
1
+ # encoding: utf-8
2
+
3
+ module Inspec::Reporters
4
+ class Junit < Base
5
+ def render
6
+ require 'rexml/document'
7
+ xml_output = REXML::Document.new
8
+ xml_output.add(REXML::XMLDecl.new)
9
+
10
+ testsuites = REXML::Element.new('testsuites')
11
+ xml_output.add(testsuites)
12
+
13
+ run_data[:profiles].each do |profile|
14
+ testsuites.add(build_profile_xml(profile))
15
+ end
16
+
17
+ formatter = REXML::Formatters::Pretty.new
18
+ formatter.compact = true
19
+ output(formatter.write(xml_output.xml_decl, ''))
20
+ output(formatter.write(xml_output.root, ''))
21
+ end
22
+
23
+ private
24
+
25
+ def build_profile_xml(profile)
26
+ profile_xml = REXML::Element.new('testsuite')
27
+ profile_xml.add_attribute('name', profile[:name])
28
+ profile_xml.add_attribute('tests', count_profile_tests(profile))
29
+ profile_xml.add_attribute('failed', count_profile_failed_tests(profile))
30
+
31
+ profile[:controls].each do |control|
32
+ next if control[:results].nil?
33
+
34
+ control[:results].each do |result|
35
+ profile_xml.add(build_result_xml(profile[:name], control, result))
36
+ end
37
+ end
38
+
39
+ profile_xml
40
+ end
41
+
42
+ def build_result_xml(profile_name, control, result)
43
+ result_xml = REXML::Element.new('testcase')
44
+ result_xml.add_attribute('name', result[:code_desc])
45
+ result_xml.add_attribute('classname', control[:title].nil? ? "#{profile_name}.Anonymous" : "#{profile_name}.#{control[:id]}")
46
+ result_xml.add_attribute('time', result[:run_time])
47
+
48
+ if result[:status] == 'failed'
49
+ failure_element = REXML::Element.new('failure')
50
+ failure_element.add_attribute('message', result[:message])
51
+ result_xml.add(failure_element)
52
+ elsif result[:status] == 'skipped'
53
+ result_xml.add_element('skipped')
54
+ end
55
+
56
+ result_xml
57
+ end
58
+
59
+ def count_profile_tests(profile)
60
+ profile[:controls].reduce(0) { |acc, elem|
61
+ acc + (elem[:results].nil? ? 0 : elem[:results].count)
62
+ }
63
+ end
64
+
65
+ def count_profile_failed_tests(profile)
66
+ profile[:controls].reduce(0) { |acc, elem|
67
+ if elem[:results].nil?
68
+ acc
69
+ else
70
+ acc + elem[:results].reduce(0) { |fail_test_total, test_case|
71
+ test_case[:status] == 'failed' ? fail_test_total + 1 : fail_test_total
72
+ }
73
+ end
74
+ }
75
+ end
76
+ end
77
+ end
@@ -49,6 +49,9 @@ module Inspec
49
49
  RunnerRspec.new(@conf)
50
50
  end
51
51
 
52
+ # parse any ad-hoc runners reporter formats
53
+ @conf = Inspec::BaseCLI.parse_reporters(@conf) if @conf[:type].nil?
54
+
52
55
  # list of profile attributes
53
56
  @attributes = []
54
57
 
@@ -101,6 +104,14 @@ module Inspec
101
104
  run_tests(with)
102
105
  end
103
106
 
107
+ def render_output(run_data)
108
+ return if @conf['reporter'].nil?
109
+
110
+ @conf['reporter'].each do |reporter|
111
+ Inspec::Reporters.render(reporter, run_data)
112
+ end
113
+ end
114
+
104
115
  def write_lockfile(profile)
105
116
  return false if !profile.writable?
106
117
 
@@ -114,7 +125,9 @@ module Inspec
114
125
  end
115
126
 
116
127
  def run_tests(with = nil)
117
- @test_collector.run(with)
128
+ status, run_data = @test_collector.run(with)
129
+ render_output(run_data)
130
+ status
118
131
  end
119
132
 
120
133
  # determine all attributes before the execution, fetch data from secrets backend
@@ -4,7 +4,7 @@
4
4
 
5
5
  require 'rspec/core'
6
6
  require 'rspec/its'
7
- require 'inspec/rspec_json_formatter'
7
+ require 'inspec/formatters'
8
8
 
9
9
  # There be dragons!! Or borgs, or something...
10
10
  # This file and all its contents cannot be unit-tested. both test-suits
@@ -33,7 +33,7 @@ module Inspec
33
33
  # @return [nil]
34
34
  def add_profile(profile)
35
35
  RSpec.configuration.formatters
36
- .find_all { |c| c.is_a? InspecRspecJson }
36
+ .find_all { |c| c.is_a?(Inspec::Formatters::Base) }
37
37
  .each do |fmt|
38
38
  fmt.add_profile(profile)
39
39
  end
@@ -45,7 +45,7 @@ module Inspec
45
45
  # @return [nil]
46
46
  def backend=(backend)
47
47
  RSpec.configuration.formatters
48
- .find_all { |c| c.is_a? InspecRspecJson }
48
+ .find_all { |c| c.is_a?(Inspec::Formatters::Base) }
49
49
  .each do |fmt|
50
50
  fmt.backend = backend
51
51
  end
@@ -74,7 +74,8 @@ module Inspec
74
74
  # @return [int] 0 if all went well; otherwise nonzero
75
75
  def run(with = nil)
76
76
  with ||= RSpec::Core::Runner.new(nil)
77
- with.run_specs(tests)
77
+ status = with.run_specs(tests)
78
+ [status, @formatter.run_data]
78
79
  end
79
80
 
80
81
  # Provide an output hash of the run's report
@@ -98,13 +99,32 @@ module Inspec
98
99
 
99
100
  private
100
101
 
101
- FORMATTERS = {
102
- 'json-min' => 'InspecRspecMiniJson',
103
- 'json' => 'InspecRspecJson',
104
- 'json-rspec' => 'InspecRspecVanilla',
105
- 'cli' => 'InspecRspecCli',
106
- 'junit' => 'InspecRspecJUnit',
107
- }.freeze
102
+ # Set optional formatters and output
103
+ #
104
+ #
105
+ def set_optional_formatters
106
+ return if @conf[:reporter].nil?
107
+ if @conf[:reporter].key?('json-rspec')
108
+ # We cannot pass in a nil output path. Rspec only accepts a valid string or a IO object.
109
+ if @conf[:reporter]['json-rspec']&.[]('file').nil?
110
+ RSpec.configuration.add_formatter(Inspec::Formatters::RspecJson)
111
+ else
112
+ RSpec.configuration.add_formatter(Inspec::Formatters::RspecJson, @conf[:reporter]['json-rspec']['file'])
113
+ end
114
+ @conf[:reporter].delete('json-rspec')
115
+ end
116
+
117
+ formats = @conf[:reporter].select { |k, _v| %w{documentation progress html}.include?(k) }
118
+ formats.each do |k, v|
119
+ # We cannot pass in a nil output path. Rspec only accepts a valid string or a IO object.
120
+ if v&.[]('file').nil?
121
+ RSpec.configuration.add_formatter(k.to_sym)
122
+ else
123
+ RSpec.configuration.add_formatter(k.to_sym, v['file'])
124
+ end
125
+ @conf[:reporter].delete(k)
126
+ end
127
+ end
108
128
 
109
129
  # Configure the output formatter and stream to be used with RSpec.
110
130
  #
@@ -116,10 +136,10 @@ module Inspec
116
136
  RSpec.configuration.output_stream = @conf['output']
117
137
  end
118
138
 
119
- format = FORMATTERS[@conf['format']] || @conf['format'] || FORMATTERS['cli']
120
- @formatter = RSpec.configuration.add_formatter(format)
139
+ @formatter = RSpec.configuration.add_formatter(Inspec::Formatters::Base)
140
+ RSpec.configuration.add_formatter(Inspec::Formatters::ShowProgress, $stderr) if @conf[:show_progress]
141
+ set_optional_formatters
121
142
  RSpec.configuration.color = @conf['color']
122
-
123
143
  setup_reporting if @conf['report']
124
144
  end
125
145
 
@@ -147,6 +147,7 @@ module Inspec
147
147
  'properties' => {
148
148
  'id' => { 'type' => 'string' },
149
149
  'profile_id' => { 'type' => %w{string null} },
150
+ 'profile_sha256' => { 'type' => 'string' },
150
151
  'status' => { 'type' => 'string' },
151
152
  'code_desc' => { 'type' => 'string' },
152
153
  'skip_message' => { 'type' => 'string', 'optional' => true },
@@ -2,7 +2,6 @@
2
2
  # author: Dominik Richter
3
3
  # author: Christoph Hartmann
4
4
 
5
- require 'rspec/core/formatters/base_text_formatter'
6
5
  require 'pry'
7
6
 
8
7
  module Inspec
@@ -4,5 +4,5 @@
4
4
  # author: Christoph Hartmann
5
5
 
6
6
  module Inspec
7
- VERSION = '1.51.0'
7
+ VERSION = '1.51.6'
8
8
  end
@@ -6,9 +6,29 @@
6
6
  module Inspec::Resources
7
7
  class Apache < Inspec.resource(1)
8
8
  name 'apache'
9
+ desc 'Use the apache InSpec audit resource to retrieve Apache environment settings.'
10
+ example "
11
+ describe apache do
12
+ its ('service') { should cmp 'apache2' }
13
+ end
14
+
15
+ describe apache do
16
+ its ('conf_dir') { should cmp '/etc/apache2' }
17
+ end
18
+
19
+ describe apache do
20
+ its ('conf_path') { should cmp '/etc/apache2/apache2.conf' }
21
+ end
22
+
23
+ describe apache do
24
+ its ('user') { should cmp 'www-data' }
25
+ end
26
+ "
9
27
 
10
28
  attr_reader :service, :conf_dir, :conf_path, :user
11
29
  def initialize
30
+ warn '[DEPRECATED] The `apache` resource is deprecated and will be removed in InSpec 3.0.'
31
+
12
32
  if inspec.os.debian?
13
33
  @service = 'apache2'
14
34
  @conf_dir = '/etc/apache2/'
@@ -9,6 +9,8 @@ require 'utils/find_files'
9
9
  module Inspec::Resources
10
10
  class ApacheConf < Inspec.resource(1)
11
11
  name 'apache_conf'
12
+ supports os_family: 'linux'
13
+ supports os_family: 'debian'
12
14
  desc 'Use the apache_conf InSpec audit resource to test the configuration settings for Apache. This file is typically located under /etc/apache2 on the Debian and Ubuntu platforms and under /etc/httpd on the Fedora, CentOS, Red Hat Enterprise Linux, and Arch Linux platforms. The configuration settings may vary significantly from platform to platform.'
13
15
  example "
14
16
  describe apache_conf do
@@ -18,9 +20,10 @@ module Inspec::Resources
18
20
 
19
21
  include FindFiles
20
22
 
23
+ attr_reader :conf_path
24
+
21
25
  def initialize(conf_path = nil)
22
- @conf_path = conf_path || inspec.apache.conf_path
23
- @conf_dir = conf_path ? File.dirname(@conf_path) : inspec.apache.conf_dir
26
+ @conf_path = conf_path || default_conf_path
24
27
  @files_contents = {}
25
28
  @content = nil
26
29
  @params = nil
@@ -63,17 +66,17 @@ module Inspec::Resources
63
66
  @params = {}
64
67
 
65
68
  # skip if the main configuration file doesn't exist
66
- file = inspec.file(@conf_path)
69
+ file = inspec.file(conf_path)
67
70
  if !file.file?
68
- return skip_resource "Can't find file \"#{@conf_path}\""
71
+ return skip_resource "Can't find file \"#{conf_path}\""
69
72
  end
70
73
 
71
74
  raw_conf = file.content
72
75
  if raw_conf.empty? && !file.empty?
73
- return skip_resource("Can't read file \"#{@conf_path}\"")
76
+ return skip_resource("Can't read file \"#{conf_path}\"")
74
77
  end
75
78
 
76
- to_read = [@conf_path]
79
+ to_read = [conf_path]
77
80
  until to_read.empty?
78
81
  raw_conf = read_file(to_read[0])
79
82
  @content += raw_conf
@@ -111,7 +114,7 @@ module Inspec::Resources
111
114
 
112
115
  includes = []
113
116
  (include_files + include_files_optional).each do |f|
114
- id = Pathname.new(f).absolute? ? f : File.join(@conf_dir, f)
117
+ id = Pathname.new(f).absolute? ? f : File.join(conf_dir, f)
115
118
  files = find_files(id, depth: 1, type: 'file')
116
119
  files += find_files(id, depth: 1, type: 'link')
117
120
 
@@ -126,8 +129,30 @@ module Inspec::Resources
126
129
  @files_contents[path] ||= inspec.file(path).content
127
130
  end
128
131
 
132
+ def conf_dir
133
+ if inspec.os.debian?
134
+ File.dirname(conf_path)
135
+ else
136
+ # On RHEL-based systems, the configuration is usually in a /conf directory
137
+ # that contains the primary config file. We assume the "config path" is the
138
+ # directory that contains the /conf directory, such as /etc/httpd, so that
139
+ # the conf.d directory can be properly located.
140
+ Pathname.new(File.dirname(conf_path)).parent.to_s
141
+ end
142
+ end
143
+
129
144
  def to_s
130
- "Apache Config #{@conf_path}"
145
+ "Apache Config #{conf_path}"
146
+ end
147
+
148
+ private
149
+
150
+ def default_conf_path
151
+ if inspec.os.debian?
152
+ '/etc/apache2/apache2.conf'
153
+ else
154
+ '/etc/httpd/conf/httpd.conf'
155
+ end
131
156
  end
132
157
  end
133
158
  end