The user can now save customizations to a named profile, which can be
loaded later.
Fixes bug #7328
---
src/bin/secstate | 10 +++++++
src/secstate/main.py | 42 ++++++++++++++++++++++++-----
src/secstate/util.py | 72 ++++++++++++++++++++-----------------------------
3 files changed, 74 insertions(+), 50 deletions(-)
diff --git a/src/bin/secstate b/src/bin/secstate
index e4bc7f0..9692c0e 100644
--- a/src/bin/secstate
+++ b/src/bin/secstate
@@ -41,6 +41,7 @@ Sub-commands:
export -- Exports imported content along with any changes that have been
made
select -- Sets a portion of the benchmark (group/rule) to be selected
deselect -- Sets a portion of the benchmark (group/rule) to be deselected
+ save -- Saves changes made to the current benchmark to a named profile
list -- List the imported benchmarks
show -- Shows the information associated with a group, rule, or
definition id
search -- Search through imported content
@@ -98,6 +99,9 @@ def main():
elif subcommand == 'remediate':
remediate(sys.argv[arg_num:])
+
+ elif subcommand == 'save':
+ save_profile(sys.argv[arg_num:])
else:
sys.stderr.write("Uknown subcommand: %(command)s" %
{'command':subcommand})
@@ -250,6 +254,12 @@ def show(arguments):
return -1
return 0
+def save_profile(arguments):
+ parser = OptionParser(usage="secstate save [options] <benchmark>
<profile name>")
+ (options, args) = parser.parse_args(arguments)
+ if not sec_instance.save_profile(args[0], args[1]):
+ return -1
+
if __name__ == '__main__':
sys.exit(main())
diff --git a/src/secstate/main.py b/src/secstate/main.py
index d38e96e..8948712 100644
--- a/src/secstate/main.py
+++ b/src/secstate/main.py
@@ -456,8 +456,8 @@ class Secstate:
return False
fp.close()
- if not bench_cfg.has_section('selections'):
- bench_cfg.add_section('selections')
+ if not bench_cfg.has_section('Custom'):
+ bench_cfg.add_section('Custom')
if not self.content.has_key(benchmark_id):
self.log.error("No benchmark %(id)s in datastore" %
{'id':benchmark_id})
@@ -494,16 +494,16 @@ class Secstate:
if bench_cfg.has_option(benchmark_id, 'profile'):
active_profile = bench_cfg.get(benchmark_id, 'profile')
if active_profile != "Custom":
- bench_cfg.set(benchmark_id, 'extends', active_profile)
+ bench_cfg.set('Custom', 'extends',
active_profile)
if item.type != oscap.xccdf.XCCDF_BENCHMARK:
- bench_cfg.set('selections', item_id, json.dumps(sel_dict))
+ bench_cfg.set('Custom', item_id, json.dumps(sel_dict))
self.log.debug("Setting %(id)s to %(val)s" %
{'id':item_id,
'val':selected})
if selected:
parent = item.parent
while parent.id != benchmark_id:
- bench_cfg.set('selections', parent_id,
json.dumps(sel_dict))
+ bench_cfg.set('Custom', parent_id,
json.dumps(sel_dict))
self.log.debug("Setting %(id)s to %(val)s" %
{'id':parent_id,
'val':selected})
parent = parent.parent
@@ -512,7 +512,7 @@ class Secstate:
if recurse:
if (item.type == oscap.xccdf.XCCDF_GROUP) or (item.type ==
oscap.xccdf.XCCDF_BENCHMARK):
for sub in xccdf_get_items(benchmark, oscap.xccdf.XCCDF_ITEM,
item.content):
- bench_cfg.set('selections', sub.id,
json.dumps(sel_dict))
+ bench_cfg.set('Custom', sub.id,
json.dumps(sel_dict))
self.log.debug("Setting %(id)s to %(val)s" %
{'id':sub.id,
'val':selected})
@@ -521,6 +521,34 @@ class Secstate:
fp.close()
return True
+ def save_profile(self, benchmark_id, profile_name):
+ if not self.content.has_key(benchmark_id):
+ self.log.error("No benchmark named %(id)s has been imported" %
{'id':benchmark_id})
+ return False
+
+ bench_cfg = ConfigParser.ConfigParser()
+ bench_cfg.optionxform = str
+ fp = open(self.content_configs[benchmark_id])
+ bench_cfg.readfp(fp)
+ fp.close()
+
+ if bench_cfg.has_section("Custom"):
+ bench_cfg.add_section(profile_name)
+ for opt,val in bench_cfg.items("Custom"):
+ bench_cfg.set(profile_name, opt, val)
+ bench_cfg.remove_section("Custom")
+ bench_cfg.set(benchmark_id, 'profile', profile_name)
+ else:
+ self.log.error("No changes have been made to the current profile")
+ return False
+
+ fp = open(self.content_configs[benchmark_id], 'w')
+ bench_cfg.write(fp)
+ fp.close()
+
+ return True
+
+
def audit(self, interpreter, args, profile=None, verbose=False, all=False, xml=None,
html=None):
"""
Function: Run an audit on the system agains the given definition model
@@ -784,7 +812,7 @@ class Secstate:
if config.has_option(benchmark.id, 'profile'):
prof = benchmark.get_item(config.get(benchmark.id,
"profile"))
if prof == None:
- self.log.error("Error loading profile %(prof)s" %
{'prof':profile})
+ self.log.error("Error loading profile %(prof)s" %
{'prof':config.get(benchmark.id, 'profile')})
return False
prof = prof.to_profile()
diff --git a/src/secstate/util.py b/src/secstate/util.py
index 214e782..783ed9e 100644
--- a/src/secstate/util.py
+++ b/src/secstate/util.py
@@ -315,24 +315,6 @@ def xccdf_rule_get_defs(rule):
return defs
-def apply_changes(benchmark, conf):
- config = ConfigParser.ConfigParser()
- config.optionxform = str
- if os.path.isfile(conf):
- try:
- fp = open(conf)
- config.readfp(fp)
- except IOError,e:
- sys.stderr.write("Error opening config file: %(err)s\n" %
{'err':e})
- return None
- fp.close()
-
- for id in config.options("selections"):
- item = benchmark.get_item(id)
- item.selected = config.getboolean('selections', id)
-
- return benchmark
-
def apply_changes_profile(benchmark, conf):
config = ConfigParser.ConfigParser()
config.optionxform = str
@@ -345,31 +327,35 @@ def apply_changes_profile(benchmark, conf):
return None
fp.close()
- if config.has_option(benchmark.id, 'extends'):
- prof = benchmark.get_item(config.get(benchmark.id,
'extends')).to_profile().clone()
- titles = prof.title
- if len(titles) > 0:
- title = titles[0]
- original_text = title.text
- title.text = "-- Customized --" + original_text
- prof.extends = config.get(benchmark.id, 'extends')
- else:
- prof = oscap.xccdf.profile_new()
- prof.id = "Custom"
-
- if config.has_section("selections"):
- for id in config.options("selections"):
- sel_dict = json.loads(config.get('selections', id))
- select = oscap.xccdf.select_new()
- select.item = id
- select.selected = sel_dict['selected']
- if sel_dict['message']:
- text = oscap.common.text_new()
- text.text = str(sel_dict['message'])
- select.add_remark(text)
- prof.add_select(select)
-
- benchmark.add_profile(prof)
+ for section in config.sections():
+ if section != benchmark.id:
+ prof = oscap.xccdf.profile_new()
+ if config.has_option(section, 'extends'):
+ original_prof = benchmark.get_item(config.get(section,
'extends')).to_profile()
+ if len(original_prof.title) > 0:
+ new_title = oscap.common.text_new()
+ new_title.text = "-- Customized --" +
original_prof.title[0].text
+ prof.add_title(new_title)
+ prof.extends = config.get(section, 'extends')
+ else:
+ new_title = oscap.common.text_new()
+ new_title.text = "Customized profile from secstate"
+ prof.add_title(new_title)
+ prof.id = section
+
+ for id,val in config.items(section):
+ if id != 'extends':
+ sel_dict = json.loads(val)
+ select = oscap.xccdf.select_new()
+ select.item = id
+ select.selected = sel_dict['selected']
+ if sel_dict['message']:
+ text = oscap.common.text_new()
+ text.text = str(sel_dict['message'])
+ select.add_remark(text)
+ prof.add_select(select)
+
+ benchmark.add_profile(prof)
return benchmark
--
1.7.2