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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+