Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@ RUN useradd -ms /bin/bash vscode \
USER vscode

# Set up bash-git-prompt
RUN git clone https://github.com/magicmonty/bash-git-prompt.git /home/vscode/.bash-git-prompt --depth 1 \
&& cat >> /home/vscode/.bashrc <<EOF
# RUN git clone https://github.com/magicmonty/bash-git-prompt.git /home/vscode/.bash-git-prompt --depth 1 \
# && cat >> /home/vscode/.bashrc <<EOF

if [ -f ~/.bash-git-prompt/gitprompt.sh ]; then
GIT_PROMPT_ONLY_IN_REPO=1
. ~/.bash-git-prompt/gitprompt.sh
fi
EOF
# if [ -f ~/.bash-git-prompt/gitprompt.sh ]; then
# GIT_PROMPT_ONLY_IN_REPO=1
# . ~/.bash-git-prompt/gitprompt.sh
# fi
# EOF

# Set the working directory
WORKDIR /workspace
5 changes: 5 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,16 @@ gemspec
gem 'rake', '~> 13.3.0'

group :tests do
gem 'puppet', ENV.fetch('PUPPET_VERSION', '~> 8.0')
gem 'puppetlabs_spec_helper', '~> 8.0'
gem 'rspec', '~> 3.12'
gem 'rspec-puppet', '~> 5.0.0'
gem 'rspec-puppet-facts', '~> 3.0'
gem 'rubocop', '~> 1.81.0'
gem 'rubocop-performance', '~> 1.26.0'
gem 'rubocop-rake', '~> 0.7.0'
gem 'rubocop-rspec', '~> 3.8.0'
gem 'syslog', require: false
end

group :development do
Expand Down
5 changes: 4 additions & 1 deletion Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
require 'bundler/gem_tasks'
require 'rspec/core/rake_task'

RSpec::Core::RakeTask.new(:spec)
RSpec::Core::RakeTask.new(:spec) do |t|
t.pattern = 'spec/**/*_spec.rb'
t.exclude_pattern = 'spec/{data,fixtures,support/**/*_spec.rb'
end

require 'rubocop/rake_task'

Expand Down
4 changes: 2 additions & 2 deletions compliance_engine.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ Gem::Specification.new do |spec|
spec.metadata['bug_tracker_uri'] = 'https://github.com/simp/rubygem-simp-compliance_engine/issues'

# Specify which files should be added to the gem when it is released.
spec.files = Dir.glob(['*.gemspec', '*.md', 'LICENSE', 'exe/*', 'lib/**/*.rb'])
spec.files = Dir.glob(['*.gemspec', '*.md', 'LICENSE', 'exe/*', 'lib/**/*.rb']).reject { |f| f.start_with?('lib/puppet/') }
spec.bindir = 'exe'
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
spec.require_paths = ['lib']

spec.add_dependency 'deep_merge', '~> 1.2'
spec.add_dependency 'irb', '~> 1.14'
spec.add_dependency 'logger', '>= 1.4', '< 2.0'
spec.add_dependency 'logger', '~> 1.4'
spec.add_dependency 'observer', '~> 0.1'
spec.add_dependency 'rubyzip', '>= 2.3', '< 4'
spec.add_dependency 'semantic_puppet', '~> 1.1'
Expand Down
26 changes: 25 additions & 1 deletion lib/compliance_engine/data.rb
Original file line number Diff line number Diff line change
Expand Up @@ -326,12 +326,36 @@ def check_mapping(profile_or_ce)
return @check_mapping[cache_key] if @check_mapping.key?(cache_key)

@check_mapping[cache_key] = checks.select do |_, check|
mapping?(check, profile_or_ce)
mapping?(check, profile_or_ce) && !filtered_by_tolerance?(check)
end
end

private

# Check if a check should be filtered out based on enforcement tolerance
#
# @param check [ComplianceEngine::Check] The check to evaluate
# @return [TrueClass, FalseClass] true if check should be filtered out
def filtered_by_tolerance?(check)
return false if enforcement_tolerance.nil?

remediation = check.remediation
return false if remediation.nil?

# Filter out disabled checks
return true if remediation['disabled']

# Filter based on risk level
if remediation['risk']&.is_a?(Array) && !remediation['risk'].empty?
risk_level = remediation['risk'][0]['level']
if risk_level && enforcement_tolerance.to_i > 0
return risk_level.to_i > enforcement_tolerance.to_i
end
end

false
end

# Get the collection variables
#
# @return [Array<Symbol>]
Expand Down
2 changes: 1 addition & 1 deletion lib/compliance_engine/version.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module ComplianceEngine
VERSION = '0.1.6'
VERSION = '0.2.0'

# Handle supported compliance data versions
class Version
Expand Down
95 changes: 95 additions & 0 deletions lib/puppet/functions/compliance_engine/enforcement.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# frozen_string_literal: true

# @summary Hiera entry point for Compliance Engine
Puppet::Functions.create_function(:'compliance_engine::enforcement') do
# @param key String The key to lookup in the Hiera data
# @return [String] The value of the key in the Hiera data
dispatch :enforcement do
param 'String[1]', :key
param 'Hash[String[1], Any]', :options
param 'Puppet::LookupContext', :context
end

require 'compliance_engine'

def enforcement(key, options, context)
ComplianceEngine.log.level = Logger::DEBUG

@compat = options['compliance_markup_compatibility']

case key
when 'lookup_options'
return context.not_found
when %r{^compliance_(?:engine|markup)::}
return context.not_found
else
return context.interpolate(context.cached_value(key)) if context.cache_has_key(key)
end

# If we have no profiles to work with, we can't do anything.
return context.not_found if profiles.empty?

if context.cache_has_key(:compliance_engine)
ComplianceEngine.log.debug('Using cached ComplianceEngine::Data object')
data = context.cached_value(:compliance_engine)
else
data = ComplianceEngine::Data.new
data.facts = closure_scope.lookupvar('facts')
data.enforcement_tolerance = enforcement_tolerance || options['enforcement_tolerance']
data.open(ComplianceEngine::EnvironmentLoader.new(*closure_scope.environment.full_modulepath.select { |path| File.directory?(path) }))

unless compliance_map.empty?
data.open(ComplianceEngine::DataLoader.new(compliance_map))
end
context.cache(:compliance_engine, data)
end

context.cache_all(data.hiera(profiles))

return context.interpolate(context.cached_value(key)) if context.cache_has_key(key)
# if data.hiera(profiles).key?(key)
# context.cache(key, data.hiera(profiles)[key])
# return context.interpolate(data.hiera(profiles)[key])
# end

context.not_found
rescue StandardError => e
# Log any exceptions that occur
ComplianceEngine.log.error("Error in compliance_engine::enforcement: #{e.message}")
ComplianceEngine.log.error(e.backtrace.join("\n"))
raise
end

def profiles
profile_list = call_function('lookup', 'compliance_engine::enforcement', { 'default_value' => [] })

# For backwards compatibility with compliance_markup.
if @compat
profile_list += call_function('lookup', 'compliance_markup::enforcement', { 'default_value' => [] })
end

profile_list.uniq
end

def compliance_map
hiera_compliance_map = call_function('lookup', 'compliance_engine::compliance_map', { 'default_value' => {} })

# For backwards compatibility with compliance_markup.
if @compat
hiera_compliance_map = DeepMerge.deep_merge!(call_function('lookup', 'compliance_markup::compliance_map', { 'default_value' => {} }), hiera_compliance_map)
end

hiera_compliance_map
end

def enforcement_tolerance
tolerance = call_function('lookup', 'compliance_engine::enforcement_tolerance', { 'default_value' => nil })

# For backwards compatibility with compliance_markup.
if @compat
tolerance = call_function('lookup', 'compliance_markup::enforcement_tolerance_level', { 'default_value' => nil }) if tolerance.nil?
end

tolerance
end
end
52 changes: 52 additions & 0 deletions metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"name": "simp-compliance_engine",
"version": "0.2.0",
"author": "Sicura",
"summary": "Hiera backend for Sicura Compliance Engine data",
"license": "Apache-2.0",
"source": "https://github.com/simp/rubygem-simp-compliance_engine",
"dependencies": [
],
"operatingsystem_support": [
{
"operatingsystem": "CentOS",
"operatingsystemrelease": [
"9"
]
},
{
"operatingsystem": "OracleLinux",
"operatingsystemrelease": [
"8",
"9"
]
},
{
"operatingsystem": "RedHat",
"operatingsystemrelease": [
"8",
"9"
]
},
{
"operatingsystem": "Rocky",
"operatingsystemrelease": [
"8",
"9"
]
},
{
"operatingsystem": "AlmaLinux",
"operatingsystemrelease": [
"8",
"9"
]
}
],
"requirements": [
{
"name": "puppet",
"version_requirement": ">= 7.0.0 < 9.0.0"
}
]
}
43 changes: 43 additions & 0 deletions spec/data/10_enforce_spec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
compliance_engine::enforcement:
- disa_stig
- nist_800_53:rev4
compliance_engine::compliance_map:
version: 2.0.0
profiles:
disa_stig:
controls:
disa_stig: true
nist_800_53:rev4:
controls:
nist_800_53:rev4: true
controls:
disa_stig: {}
nist_800_53:rev4: {}
checks:
oval:com.puppet.test.disa.useradd_shells:
type: puppet-class-parameter
controls:
disa_stig: true
identifiers:
FOO2:
- FOO2
BAR2:
- BAR2
settings:
parameter: useradd::shells
value:
- "/bin/disa"
oval:com.puppet.test.nist.useradd_shells:
type: puppet-class-parameter
controls:
nist_800_53:rev4: true
identifiers:
FOO2:
- FOO2
BAR2:
- BAR2
settings:
parameter: useradd::shells
value:
- "/bin/nist"
4 changes: 4 additions & 0 deletions spec/data/common.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
compliance_engine::enforcement:
# - test_profile
- "%{facts.target_compliance_profile}"
6 changes: 6 additions & 0 deletions spec/data/compliance_engine-tolerance-100.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
compliance_engine::enforcement:
- "%{facts.target_compliance_profile}"

# Hardcoded integer value for tolerance - Hiera interpolation would produce a string
compliance_engine::enforcement_tolerance: 100
6 changes: 6 additions & 0 deletions spec/data/compliance_engine-tolerance-25.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
compliance_engine::enforcement:
- "%{facts.target_compliance_profile}"

# Hardcoded integer value for tolerance - Hiera interpolation would produce a string
compliance_engine::enforcement_tolerance: 25
6 changes: 6 additions & 0 deletions spec/data/compliance_engine-tolerance-60.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
compliance_engine::enforcement:
- "%{facts.target_compliance_profile}"

# Hardcoded integer value for tolerance - Hiera interpolation would produce a string
compliance_engine::enforcement_tolerance: 60
9 changes: 9 additions & 0 deletions spec/data/compliance_engine-tolerance.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
compliance_engine::enforcement:
- "%{facts.target_compliance_profile}"

# Note: Hiera interpolation always produces strings, so for testing enforcement
# tolerance (which requires Integer), use the dedicated hieradata files:
# - compliance-engine-tolerance-25.yaml
# - compliance-engine-tolerance-60.yaml
# - compliance-engine-tolerance-100.yaml
19 changes: 19 additions & 0 deletions spec/data/compliance_engine.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
compliance_engine::validate_profiles:
- "%{facts.target_compliance_profile}"

# Needed for catalog inspection to ensure valid data
compliance_engine::report_on_client: true
compliance_engine::report_on_server: false
compliance_engine::report_types:
- 'compliant'
- 'non_compliant'
- 'unknown_parameters'
- 'unknown_resources'

# Ideally, this would be the same as the validation array but you may want to
# do something different based on your test requirements
compliance_engine::enforcement:
- "%{facts.target_compliance_profile}"

compliance_engine::enforcement_tolerance: "%{facts.target_enforcement_tolerance}"
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
# Some very basic compliance checks designed for the tests
#
# These should all pass

version: 2.0.0

compliance_markup::enforcement:
- test_profile

compliance_markup::enforcement_tolerance_level: 40

profiles:
test_profile:
controls:
test_control: true

controls:
test_control: {}

checks:
oval:test4:
type: puppet-class-parameter
settings:
parameter: test4::list1
value:
- '\\-- not_a_knockout'
controls:
test_control: true
identifiers:
- 'ESC_KNOCKOUT'
Loading
Loading