From ddf8194397bc51165de09a600da2016366fb8632 Mon Sep 17 00:00:00 2001 From: thomashaw Date: Tue, 8 Mar 2016 18:54:15 +0000 Subject: [PATCH] Configuration changes Pulled System.rb out to individual classes. --- lib/base_manager.rb | 9 ++ lib/configuration.rb | 73 +++++++++++ lib/constants.rb | 3 + lib/erb_controller.rb | 11 ++ lib/filecreator.rb | 34 ++---- lib/network_manager.rb | 32 +++++ lib/objects/base_box.rb | 3 + lib/objects/network.rb | 24 ++++ lib/objects/service.rb | 22 ++++ lib/objects/system.rb | 28 +++++ lib/objects/vulnerability.rb | 10 +- lib/service_manager.rb | 47 +++++++ lib/system.rb | 231 ----------------------------------- lib/systemreader.rb | 25 ++-- secgen.rb | 12 +- tests/checkifequal.rb | 2 +- 16 files changed, 289 insertions(+), 277 deletions(-) create mode 100644 lib/base_manager.rb create mode 100644 lib/configuration.rb create mode 100644 lib/erb_controller.rb create mode 100644 lib/network_manager.rb create mode 100644 lib/objects/base_box.rb create mode 100644 lib/objects/network.rb create mode 100644 lib/objects/service.rb create mode 100644 lib/objects/system.rb create mode 100644 lib/service_manager.rb delete mode 100644 lib/system.rb diff --git a/lib/base_manager.rb b/lib/base_manager.rb new file mode 100644 index 000000000..5df054be1 --- /dev/null +++ b/lib/base_manager.rb @@ -0,0 +1,9 @@ +class BaseManager + def self.generate_base(system,bases) + # takes a sample from bases.xml and then assigns it to system + box = bases.sample + system.basebox = box.vagrantbase + system.url = box.url + return system + end +end \ No newline at end of file diff --git a/lib/configuration.rb b/lib/configuration.rb new file mode 100644 index 000000000..f6ce67f7c --- /dev/null +++ b/lib/configuration.rb @@ -0,0 +1,73 @@ +require_relative 'systemreader.rb' + +class Configuration + + # populates the system class with an array of System objects. + def initialize + @systemreader = SystemReader.new + @systems = init_systems() + end + + def get_systems + if @systems.empty? + init_systems() + end + return @systems + end + + def init_systems() + @systems = @systemreader.parse_systems + end + + def self.networks + if defined? @@networks + return @@networks + end + return @@networks = _get_list(NETWORKS_XML, "//networks/network", Network) + end + + def self.bases + if defined? @@bases + return @@bases + end + return @@bases = _get_list(BASE_XML, "//bases/base", Basebox) + end + + def self.vulnerabilities + if defined? @@vulnerabilities + return @@vulnerabilities + end + return @@vulnerabilities = _get_list(VULN_XML, "//vulnerabilities/vulnerability", Vulnerability) + end + + def self.services + if defined? @@services + return @@services + end + return @@services = _get_list(SERVICES_XML, "//services/service", Service) + end + + def self._get_list(xmlfile, xpath, cls) + itemlist = [] + + doc = Nokogiri::XML(File.read(xmlfile)) + doc.xpath(xpath).each do |item| + # new class e.g networks + obj = cls.new + # checks to see if there are children puppet and add string to obj.puppets + # move this to vulnerabilities/services classes? + if defined? obj.puppets + item.xpath("puppets/puppet").each { |c| obj.puppets << c.text.strip if not c.text.strip.empty? } + item.xpath("ports/port").each { |c| obj.ports << c.text.strip if not c.text.strip.empty? } + end + # too specific move to vuln class end + item.each do |attr, value| + + obj.send "#{attr}=", value + end + # vulnerability item + itemlist << obj + end + return itemlist + end +end \ No newline at end of file diff --git a/lib/constants.rb b/lib/constants.rb index 89c7bc07a..f248f040a 100644 --- a/lib/constants.rb +++ b/lib/constants.rb @@ -8,6 +8,7 @@ BASE_XML = "#{ROOT_DIR}/xml/bases.xml" MOUNT_DIR = "#{ROOT_DIR}/mount/" BUILD_DIR = "#{ROOT_DIR}/modules/build/" MOUNT_PUPPET_DIR = "#{ROOT_DIR}/mount/puppet" +PROJECTS_DIR = "#{ROOT_DIR}/projects" #PATH CONSTANTS MODULES_PATH = "#{ROOT_DIR}/modules/" @@ -21,3 +22,5 @@ AVAILABLE_CVE_NUMBERS = [] #VAGRANT_FILE_CONSTANTS PATH_TO_CLEANUP = "#{ROOT_DIR}/modules/build/puppet/" +VAGRANT_TEMPLATE_FILE = "#{ROOT_DIR}/lib/templates/vagrantbase.erb" +REPORT_TEMPLATE_FILE = "#{ROOT_DIR}/lib/templates/report.erb" diff --git a/lib/erb_controller.rb b/lib/erb_controller.rb new file mode 100644 index 000000000..f44808fa2 --- /dev/null +++ b/lib/erb_controller.rb @@ -0,0 +1,11 @@ +class ERBController + +# ERB Controller initializes the system and returns the binding when mapping .erb files + attr_accessor :systems + def initialize + @systems = [] + end + def get_binding + return binding + end +end \ No newline at end of file diff --git a/lib/filecreator.rb b/lib/filecreator.rb index 39fb45c36..a2a81cd1c 100644 --- a/lib/filecreator.rb +++ b/lib/filecreator.rb @@ -1,21 +1,18 @@ require 'erb' +require_relative 'erb_controller' require_relative 'constants' - - -VAGRANT_TEMPLATE_FILE = "#{ROOT_DIR}/lib/templates/vagrantbase.erb" -REPORT_TEMPLATE_FILE = "#{ROOT_DIR}/lib/templates/report.erb" - -PROJECTS_DIR = "#{ROOT_DIR}/projects" +require_relative 'configuration' class FileCreator # Creates project directory, uses .erb files to create a report and the vagrant file that will be used # to create the virtual machines - def initialize(systems) - @systems = systems + def initialize(config) + @configuration = config end - def generate(system) - Dir::mkdir("#{PROJECTS_DIR}") unless File.exists?("#{PROJECTS_DIR}") + def generate() + systems = @configuration.get_systems + Dir::mkdir("#{PROJECTS_DIR}") unless File.exists?("#{PROJECTS_DIR}") count = Dir["#{PROJECTS_DIR}/*"].length build_number = count.next @@ -31,7 +28,7 @@ class FileCreator %x[#{command}] controller = ERBController.new - controller.systems = system + controller.systems = systems vagrant_template = ERB.new(File.read(VAGRANT_TEMPLATE_FILE), 0, '<>') File.delete("#{PROJECTS_DIR}/Project#{build_number}/Vagrantfile") puts "#{PROJECTS_DIR}/Project#{build_number}/Vagrantfile file has been created" @@ -44,17 +41,4 @@ class FileCreator return build_number end -end - - -class ERBController - -# ERB Controller initializes the system and returns the binding when mapping .erb files - attr_accessor :systems - def initialize - @systems = [] - end - def get_binding - return binding - end -end +end \ No newline at end of file diff --git a/lib/network_manager.rb b/lib/network_manager.rb new file mode 100644 index 000000000..bbfa3fb74 --- /dev/null +++ b/lib/network_manager.rb @@ -0,0 +1,32 @@ +class NetworkManager + # the user will either specify a blank misc type or a knownnetwork type + def self.process(networks,valid_network) + new_networks = {} + # intersection of valid networks / user defined networks + legal_networks = valid_network & networks + networks.each do |network| + # checks to see string is blank if so valid misc into a new hash map of vulnerabilities + if network.name == "" + random = valid_network.sample + new_networks[random.id] = random + else + has_found = false + # shuffle randomly selects first match + legal_networks.shuffle.each do |valid| + if network.name == valid.name + network.range = valid.range unless not network.range.empty? + # valid misc into a new hash map of networks + new_networks[network.id] = network + has_found = true + break + end + end + if not has_found + p "Network was not found please check the xml scenario.xml" + exit + end + end + end + return new_networks.values + end +end \ No newline at end of file diff --git a/lib/objects/base_box.rb b/lib/objects/base_box.rb new file mode 100644 index 000000000..da9f51d03 --- /dev/null +++ b/lib/objects/base_box.rb @@ -0,0 +1,3 @@ +class Basebox + attr_accessor :name, :os, :distro, :vagrantbase, :url +end \ No newline at end of file diff --git a/lib/objects/network.rb b/lib/objects/network.rb new file mode 100644 index 000000000..dbbcb6170 --- /dev/null +++ b/lib/objects/network.rb @@ -0,0 +1,24 @@ +class Network + attr_accessor :name, :range + + def initialize(name="", range="") + @name = name + @range = range + end + + def id + hash = @name + @range + return hash + # return string that connects everything to 1 massive string + end + + def eql? other + # checks if name matches networks.xml from scenario.xml + other.kind_of?(self.class) && @name == other.name + end + + def hash + @type.hash + end + +end \ No newline at end of file diff --git a/lib/objects/service.rb b/lib/objects/service.rb new file mode 100644 index 000000000..33c998841 --- /dev/null +++ b/lib/objects/service.rb @@ -0,0 +1,22 @@ +class Service + attr_accessor :name, :type, :details, :puppets + + def initialize(name="", type="", details="", puppets=[]) + @name = name + @type = type + @details = details + @puppets = puppets + end + + def eql? other + other.kind_of?(self.class) && @type == other.type + end + + def hash + @type.hash + end + + def id + return @type + end +end \ No newline at end of file diff --git a/lib/objects/system.rb b/lib/objects/system.rb new file mode 100644 index 000000000..f3563a32c --- /dev/null +++ b/lib/objects/system.rb @@ -0,0 +1,28 @@ +class System + # can access from outside of class + attr_accessor :id, :os, :url,:basebox, :networks, :vulns, :services + + #initalizes system variables + def initialize(id, os, basebox, url, vulns=[], networks=[], services=[]) + @id = id + @os = os + @url = url + @basebox = basebox + @vulns = vulns + @networks = networks + @services = services + end + + def is_valid_base + valid_base = Configuration.bases + + valid_base.each do |b| + if @basebox == b.vagrantbase + @url = b.url + return true + end + end + return false + end + +end \ No newline at end of file diff --git a/lib/objects/vulnerability.rb b/lib/objects/vulnerability.rb index 0ea68d4d5..c34396203 100644 --- a/lib/objects/vulnerability.rb +++ b/lib/objects/vulnerability.rb @@ -3,11 +3,6 @@ require_relative('../constants.rb') class Vulnerability attr_accessor :type, :privilege, :access ,:puppets, :details, :ports, :name, :cve, :files, :scripts - def eql? other - # checks if type matches vulns.xml from scenario.xml - other.kind_of?(self.class) && @type == other.type - end - def initialize(type='', privilege='', access='', puppets=[], details='', ports=[], platform ='', name='', cve='', files=[], scripts=[]) @type = type @privilege = privilege @@ -22,6 +17,11 @@ class Vulnerability @scripts = scripts end + def eql? other + # checks if type matches vulns.xml from scenario.xml + other.kind_of?(self.class) && @type == other.type + end + def id return @type + @privilege + @access end diff --git a/lib/service_manager.rb b/lib/service_manager.rb new file mode 100644 index 000000000..f9a5cda7d --- /dev/null +++ b/lib/service_manager.rb @@ -0,0 +1,47 @@ +class ServiceManager + # secure services are randomly selected from the definitions in services.xml (secure_services) + # based on the attributes optionally specified in scenario.xml (want_services) + # However, if the service type has already had a vulnerability assigned (selected_vulns), it is ignored here + def self.process(want_services, secure_services, selected_vulns=[]) + return_services = {} + legal_services = secure_services.clone + wanted_services = want_services.clone + + # remove all services that have already been selected as vulns (from both the wanted and secure lists) + selected_vulns.each do |a_vuln| + legal_services.delete_if{|x| x.type == a_vuln.type} + wanted_services.delete_if{|x| x.type == a_vuln.type} + end + + wanted_services.each do |service_query| + + # select based on selected type... + + # copy services array + search_list = legal_services.clone + # shuffle order of available secure services + search_list.shuffle! + # remove all the services that don't match the current selection (type, etc) + if service_query.type.length > 0 + puts "Searching for service matching type: " + service_query.type + search_list.delete_if{|x| x.type != service_query.type} + end + + if search_list.length == 0 + STDERR.puts "Matching service was not found please check the xml scenario.xml" + STDERR.puts "(note: you can only have one of each type of service per system)" + exit + else + # use from the top of the top of the randomised list + return_services[service_query.id] = search_list[0] + if search_list[0].type.length > 0 + puts "Selected secure service : " + search_list[0].type + end + + # enforce only one of any service type (remove from available) + legal_services.delete_if{|x| x.type == service_query.type} + end + end + return return_services.values + end +end \ No newline at end of file diff --git a/lib/system.rb b/lib/system.rb deleted file mode 100644 index ad38614b9..000000000 --- a/lib/system.rb +++ /dev/null @@ -1,231 +0,0 @@ -require 'nokogiri' -require_relative 'constants' - - -class System - # can access from outside of class - attr_accessor :id, :os, :url,:basebox, :networks, :vulns, :services - - #initalizes system variables - def initialize(id, os, basebox, url, vulns=[], networks=[], services=[]) - @id = id - @os = os - @url = url - @basebox = basebox - @vulns = vulns - @networks = networks - @services = services - end - - def is_valid_base - valid_base = Conf.bases - - valid_base.each do |b| - if @basebox == b.vagrantbase - @url = b.url - return true - end - end - return false - end -end - - -class Network - attr_accessor :name, :range - - def initialize(name="", range="") - @name = name - @range = range - end - - def id - hash = @name + @range - return hash - # return string that connects everything to 1 massive string - end - - def eql? other - # checks if name matches networks.xml from scenario.xml - other.kind_of?(self.class) && @name == other.name - end - - def hash - @type.hash - end -end - -class Service - attr_accessor :name, :type, :details, :puppets - - def initialize(name="", type="", details="", puppets=[]) - @name = name - @type = type - @details = details - @puppets = puppets - end - - def eql? other - other.kind_of?(self.class) && @type == other.type - end - - def hash - @type.hash - end - - def id - return @type - end - -end - -class ServiceManager - # secure services are randomly selected from the definitions in services.xml (secure_services) - # based on the attributes optionally specified in scenario.xml (want_services) - # However, if the service type has already had a vulnerability assigned (selected_vulns), it is ignored here - def self.process(want_services, secure_services, selected_vulns=[]) - return_services = {} - legal_services = secure_services.clone - wanted_services = want_services.clone - - # remove all services that have already been selected as vulns (from both the wanted and secure lists) - selected_vulns.each do |a_vuln| - legal_services.delete_if{|x| x.type == a_vuln.type} - wanted_services.delete_if{|x| x.type == a_vuln.type} - end - - wanted_services.each do |service_query| - - # select based on selected type... - - # copy services array - search_list = legal_services.clone - # shuffle order of available secure services - search_list.shuffle! - # remove all the services that don't match the current selection (type, etc) - if service_query.type.length > 0 - puts "Searching for service matching type: " + service_query.type - search_list.delete_if{|x| x.type != service_query.type} - end - - if search_list.length == 0 - STDERR.puts "Matching service was not found please check the xml scenario.xml" - STDERR.puts "(note: you can only have one of each type of service per system)" - exit - else - # use from the top of the top of the randomised list - return_services[service_query.id] = search_list[0] - if search_list[0].type.length > 0 - puts "Selected secure service : " + search_list[0].type - end - - # enforce only one of any service type (remove from available) - legal_services.delete_if{|x| x.type == service_query.type} - end - end - return return_services.values - end -end - -class NetworkManager - # the user will either specify a blank misc type or a knownnetwork type - def self.process(networks,valid_network) - new_networks = {} - # intersection of valid networks / user defined networks - legal_networks = valid_network & networks - networks.each do |network| - # checks to see string is blank if so valid misc into a new hash map of vulnerabilities - if network.name == "" - random = valid_network.sample - new_networks[random.id] = random - else - has_found = false - # shuffle randomly selects first match - legal_networks.shuffle.each do |valid| - if network.name == valid.name - network.range = valid.range unless not network.range.empty? - # valid misc into a new hash map of networks - new_networks[network.id] = network - has_found = true - break - end - end - if not has_found - p "Network was not found please check the xml scenario.xml" - exit - end - end - end - return new_networks.values - end -end - -class Basebox - attr_accessor :name, :os, :distro, :vagrantbase, :url -end - -class BaseManager - def self.generate_base(system,bases) - # takes a sample from bases.xml and then assigns it to system - box = bases.sample - system.basebox = box.vagrantbase - system.url = box.url - return system - end -end - -class Conf - # this class uses nokogiri to grab all of the information from misc.xml, bases.xml, and vulns.xml - # then adds them to their specific class to do checking for legal in Manager.process - def self.networks - if defined? @@networks - return @@networks - end - return @@networks = self._get_list(NETWORKS_XML, "//networks/network", Network) - end - - def self.bases - if defined? @@bases - return @@bases - end - return @@bases = self._get_list(BASE_XML, "//bases/base", Basebox) - end - - def self.vulnerabilities - if defined? @@vulnerabilities - return @@vulnerabilities - end - return @@vulnerabilities = self._get_list(VULN_XML, "//vulnerabilities/vulnerability", Vulnerability) - end - - def self.services - if defined? @@services - return @@services - end - return @@services = self._get_list(SERVICES_XML, "//services/service", Service) - end - - def self._get_list(xmlfile, xpath, cls) - itemlist = [] - - doc = Nokogiri::XML(File.read(xmlfile)) - doc.xpath(xpath).each do |item| - # new class e.g networks - obj = cls.new - # checks to see if there are children puppet and add string to obj.puppets - # move this to vulnerabilities/services classes? - if defined? obj.puppets - item.xpath("puppets/puppet").each { |c| obj.puppets << c.text.strip if not c.text.strip.empty? } - item.xpath("ports/port").each { |c| obj.ports << c.text.strip if not c.text.strip.empty? } - end - # too specific move to vuln class end - item.each do |attr, value| - - obj.send "#{attr}=", value - end - # vulnerability item - itemlist << obj - end - return itemlist - end -end diff --git a/lib/systemreader.rb b/lib/systemreader.rb index 79e568dcd..c44567d2b 100644 --- a/lib/systemreader.rb +++ b/lib/systemreader.rb @@ -1,18 +1,25 @@ -require_relative 'system.rb' -require_relative 'objects/vulnerability' +require_relative 'configuration.rb' +require_relative 'network_manager.rb' +require_relative 'service_manager.rb' +require_relative 'base_manager.rb' require_relative 'helpers/vulnerability_processor' +require_relative 'objects/base_box' +require_relative 'objects/network' +require_relative 'objects/service' +require_relative 'objects/system' +require_relative 'objects/vulnerability' + class SystemReader # initializes systems xml from BOXES_XML const - def initialize(systems_xml) - @systems_xml = systems_xml + def initialize() @vulnerability_processor = VulnerabilityProcessor.new end # uses nokogiri to extract all system information from scenario.xml will add it to the system class after # checking if the vulnerabilities / networks exist from system.rb - def systems + def parse_systems systems = [] - doc = Nokogiri::XML(File.read(@systems_xml)) + doc = Nokogiri::XML(File.read(SCENARIO_XML)) doc.xpath("//systems/system").each do |system| id = system["id"] os = system["os"] @@ -51,9 +58,9 @@ class SystemReader new_vulns = @vulnerability_processor.process(vulns) #puts new_vulns.inspect - new_networks = NetworkManager.process(networks, Conf.networks) + new_networks = NetworkManager.process(networks, Configuration.networks) # pass in the already selected set of vulnerabilities, and additional secure services to find - new_services = ServiceManager.process(services, Conf.services, new_vulns) + new_services = ServiceManager.process(services, Configuration.services, new_vulns) s = System.new(id, os, basebox, url, new_vulns, new_networks, new_services) if s.is_valid_base == false @@ -64,4 +71,4 @@ class SystemReader end return systems end -end +end \ No newline at end of file diff --git a/secgen.rb b/secgen.rb index 6475d65e5..0691b39f4 100644 --- a/secgen.rb +++ b/secgen.rb @@ -25,14 +25,14 @@ end def build_config puts 'Reading configuration file for virtual machines you want to create' - # uses nokogoiri to grab all the system information from scenario.xml - systems = SystemReader.new(SCENARIO_XML).systems + # Initialise configuration + config = Configuration.new() - puts 'Creating vagrant file' + puts 'Creating vagrant file' # create's vagrant file / report a starts the vagrant installation' - create_files = FileCreator.new(systems) - build_number = create_files.generate(systems) - return build_number + file_creator = FileCreator.new(config) + build_number = file_creator.generate() + return build_number end def build_vms(build_number) diff --git a/tests/checkifequal.rb b/tests/checkifequal.rb index a67972947..3c0b6c979 100644 --- a/tests/checkifequal.rb +++ b/tests/checkifequal.rb @@ -52,7 +52,7 @@ class TestXMLIsEqual < Test::Unit::TestCase if empty_type.type == "" p empty_type - vuln = generate_vulnerability(empty_type,Conf.vulnerabilities,dummy_list) + vuln = generate_vulnerability(empty_type, Configuration.vulnerabilities, dummy_list) assert_not_match(vuln,"") end end