diff --git a/lib/helpers/constants.rb b/lib/helpers/constants.rb index 09fa27527..41150b236 100644 --- a/lib/helpers/constants.rb +++ b/lib/helpers/constants.rb @@ -1,3 +1,6 @@ +# ONE global variable +$datastore = {} + ## FILE / DIR CONSTANTS ## # Root directory of SecGen file structure diff --git a/lib/objects/module.rb b/lib/objects/module.rb index 43482cbe3..ec6148f03 100644 --- a/lib/objects/module.rb +++ b/lib/objects/module.rb @@ -12,10 +12,12 @@ class Module # XML validity ensures valid and complete information. attr_accessor :write_to_module_with_id # the module instance that this module writes to + attr_accessor :write_to_datastore # the datastore to store the result to attr_accessor :write_output_variable # the variable/fact written to attr_accessor :output # the result of local processing attr_accessor :unique_id # the unique id for this module *instance* attr_accessor :received_inputs # any locally calculated inputs fed into this module instance + attr_accessor :received_datastores # any datastores to be fed into this module instance attr_accessor :conflicts attr_accessor :requires @@ -35,6 +37,7 @@ class Module self.output = [] self.write_to_module_with_id = write_output_variable = '' self.received_inputs = {} + self.received_datastores = {} self.default_inputs_selectors = {} self.default_inputs_literals = {} diff --git a/lib/objects/system.rb b/lib/objects/system.rb index a61798e7e..5846811c5 100644 --- a/lib/objects/system.rb +++ b/lib/objects/system.rb @@ -32,7 +32,7 @@ class System # for each module specified in the scenario module_selectors.each do |module_filter| - selected_modules += select_modules(module_filter.module_type, module_filter.attributes, available_modules, selected_modules, module_filter.unique_id, module_filter.write_output_variable, module_filter.write_to_module_with_id, module_filter.received_inputs, module_filter.default_inputs_literals) + selected_modules += select_modules(module_filter.module_type, module_filter.attributes, available_modules, selected_modules, module_filter.unique_id, module_filter.write_output_variable, module_filter.write_to_module_with_id, module_filter.received_inputs, module_filter.default_inputs_literals, module_filter.write_to_datastore, module_filter.received_datastores) end selected_modules @@ -64,7 +64,7 @@ class System # returns a list containing a module (plus any default input modules and dependencies recursively) of the module type with the required attributes # modules are selected from the list of available modules and will be checked against previously selected modules for conflicts # raises an exception when unable to resolve and the retry limit has not been reached - def select_modules(module_type, required_attributes, available_modules, previously_selected_modules, unique_id, write_outputs_to, write_to_module_with_id, received_inputs, default_inputs_literals) + def select_modules(module_type, required_attributes, available_modules, previously_selected_modules, unique_id, write_outputs_to, write_to_module_with_id, received_inputs, default_inputs_literals, write_to_datastore, received_datastores) default_modules_to_add = [] search_list = available_modules.clone @@ -101,10 +101,23 @@ class System # propagate module relationships established when the filter was created selected.write_output_variable = write_outputs_to selected.write_to_module_with_id = write_to_module_with_id + selected.write_to_datastore = write_to_datastore selected.unique_id = unique_id selected.received_inputs = received_inputs + selected.received_datastores = received_datastores selected.default_inputs_literals = selected.default_inputs_literals.merge(default_inputs_literals) + # feed in input from any received datastores + if selected.received_datastores != {} + Print.verbose "Receiving datastores: #{selected.received_datastores}" + selected.received_datastores.each do |input_key, datastore_value| + datastore_value.each do |datastore| + (received_inputs[input_key] ||=[]).push(*$datastore[datastore]) + Print.verbose "Adding #{input_key} - #{datastore} (#{$datastore[datastore]})" + end + end + end + # if no input received from the scenario, apply default input values/modules default_modules_to_add += select_default_modules(selected, available_modules, previously_selected_modules + [selected]) @@ -131,6 +144,12 @@ class System selected.output = JSON.parse(`ruby #{selected.local_calc_file} #{args_string}`.chomp) end + # store the output of the module into a datastore, if specified + if selected.write_to_datastore && selected.write_to_datastore.size != 0 + ($datastore[selected.write_to_datastore] ||=[]).push(*selected.output) + Print.verbose "Storing into datastore: #{selected.write_to_datastore} = #{$datastore[selected.write_to_datastore].to_s}" + end + # add any modules that the selected module requires dependencies = select_required_modules(selected, available_modules, previously_selected_modules + default_modules_to_add + [selected]) end @@ -178,7 +197,7 @@ class System default_module_selectors_to_add.each do |module_to_add| module_to_add.write_to_module_with_id = selected.unique_id - default_modules_to_add.concat select_modules(module_to_add.module_type, module_to_add.attributes, available_modules, previously_selected_modules + default_modules_to_add, module_to_add.unique_id, module_to_add.write_output_variable, module_to_add.write_to_module_with_id, module_to_add.received_inputs, module_to_add.default_inputs_literals) + default_modules_to_add.concat select_modules(module_to_add.module_type, module_to_add.attributes, available_modules, previously_selected_modules + default_modules_to_add, module_to_add.unique_id, module_to_add.write_output_variable, module_to_add.write_to_module_with_id, module_to_add.received_inputs, module_to_add.default_inputs_literals, module_to_add.write_to_datastore, module_to_add.received_datastores) end end else @@ -230,7 +249,7 @@ class System Print.verbose "Dependency satisfied by previously selected module: #{existing.printable_name}" else Print.verbose 'Adding required modules...' - modules_to_add += select_modules('any', required, available_modules, modules_to_add + selected_modules, '', '', '', {}, {}) + modules_to_add += select_modules('any', required, available_modules, modules_to_add + selected_modules, '', '', '', {}, {}, {}, {}) end end modules_to_add diff --git a/lib/readers/system_reader.rb b/lib/readers/system_reader.rb index e79c56aae..094d108f1 100644 --- a/lib/readers/system_reader.rb +++ b/lib/readers/system_reader.rb @@ -50,6 +50,19 @@ class SystemReader system_attributes["#{attr.name}"] = attr.text unless attr.text.nil? || attr.text == '' end + # literal values to store directly in a datastore + system_node.xpath('*[@into_datastore]/value').each do |value| + name = value.xpath('../@into_datastore').to_s + ($datastore[name] ||= []).push(value.text) + end + + # datastore in a datastore + if system_node.xpath('//*[@into_datastore]/datastore').to_s != "" + Print.err "WARNING: a datastore cannot capture the values from another datastore (this will be ignored)" + Print.err "The scenario has datastore(s) that try to save directly into another datastore -- currently this is only possible via an encoder" + sleep 2 + end + # for each module selection system_node.xpath('//vulnerability | //service | //utility | //network | //base | //encoder | //generator').each do |module_node| # create a selector module, which is a regular module instance used as a placeholder for matching requirements @@ -60,10 +73,18 @@ class SystemReader # check if we need to be sending the module output to another module module_node.xpath('parent::input').each do |input| # Parent is input -- track that we need to send write value somewhere - input.xpath('..').each do |input_parent| - module_selector.write_output_variable = input.xpath('@into').to_s - module_selector.write_to_module_with_id = input_parent.path.gsub(/[^a-zA-Z0-9]/, '') + # if we need to feed results to parent module + if input.xpath('@into').to_s + input.xpath('..').each do |input_parent| + module_selector.write_output_variable = input.xpath('@into').to_s + module_selector.write_to_module_with_id = input_parent.path.gsub(/[^a-zA-Z0-9]/, '') + end end + # check if we need to send the module output to a datastore + if input.xpath('@into_datastore').to_s + module_selector.write_to_datastore = input.xpath('@into_datastore').to_s + end + end # check if we are being passed an input *literal value* @@ -74,6 +95,14 @@ class SystemReader (module_selector.received_inputs[variable] ||= []).push(value) end + # check if we are being passed a datastore as input + module_node.xpath('input/datastore').each do |input_value| + variable = input_value.xpath('../@into').to_s + value = input_value.text + Print.verbose " -- datastore: #{variable} = #{value}" + (module_selector.received_datastores[variable] ||= []).push(value) + end + module_node.xpath('@*').each do |attr| module_selector.attributes["#{attr.name}"] = [attr.text] unless attr.text.nil? || attr.text == '' diff --git a/lib/schemas/scenario_schema.xsd b/lib/schemas/scenario_schema.xsd index 5eaa44580..4822b958a 100644 --- a/lib/schemas/scenario_schema.xsd +++ b/lib/schemas/scenario_schema.xsd @@ -21,9 +21,11 @@ + + @@ -37,6 +39,7 @@ + diff --git a/lib/templates/Vagrantfile.erb b/lib/templates/Vagrantfile.erb index 3cc558cfd..f1e71ec41 100644 --- a/lib/templates/Vagrantfile.erb +++ b/lib/templates/Vagrantfile.erb @@ -33,6 +33,9 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| end %> end + # SecGen datastore + # <%= JSON.generate($datastore) %> + # SecGen modules <% system.module_selections.each do |selected_module| -%> diff --git a/scenarios/datastore_examples/example.xml b/scenarios/datastore_examples/example.xml new file mode 100644 index 000000000..154821121 --- /dev/null +++ b/scenarios/datastore_examples/example.xml @@ -0,0 +1,27 @@ + + + + + + example_server + + + + BusinessName1 + + + + + + + business_name + + + + + + + + diff --git a/scenarios/datastore_examples/precalculate_strings.xml b/scenarios/datastore_examples/precalculate_strings.xml new file mode 100644 index 000000000..88e20661a --- /dev/null +++ b/scenarios/datastore_examples/precalculate_strings.xml @@ -0,0 +1,33 @@ + + + + + + example_server + + + + BusinessName1 + + + + + cliffe + + + + + + + business_name + username + + + + + + + + diff --git a/scenarios/datastore_examples/store_flags.xml b/scenarios/datastore_examples/store_flags.xml new file mode 100644 index 000000000..d6222c91d --- /dev/null +++ b/scenarios/datastore_examples/store_flags.xml @@ -0,0 +1,32 @@ + + + + + + example_server + + + + + + + + + + + + + + + + + + + + + + + +