class Dradis::Plugins::Projects::Upload::V1::Template::Importer
Constants
- ATTACHMENT_URL
Attributes
attachment_notes[RW]
logger[RW]
pending_changes[RW]
users[RW]
Public Instance Methods
post_initialize(args={})
click to toggle source
# File lib/dradis/plugins/projects/upload/v1/template.rb, line 10 def post_initialize(args={}) @lookup_table = { categories: {}, nodes: {}, issues: {} } @pending_changes = { # If the note has an attachment screenshot (i.e. !.../nodes/i/attachments/...!) # we will fix the URL to point to the new Node ID. # # WARNING: we need a lookup table because one note may be referencing a # different (yet unprocessed) node's attachments. attachment_notes: [], # evidence is parsed when nodes are parsed, but cannot be saved until # issues have been created. Therefore, parse evidence into arrays until # time for creation evidence: [], # likewise we also need to hold on to the XML about evidence activities # and comments until after the evidence has been saved evidence_activity: [], evidence_comments: [], # all children nodes, we will need to find the ID of their new parents. orphan_nodes: [] } end
Private Instance Methods
create_activities(trackable, xml_trackable)
click to toggle source
# File lib/dradis/plugins/projects/upload/v1/template.rb, line 47 def create_activities(trackable, xml_trackable) xml_trackable.xpath('activities/activity').each do |xml_activity| # if 'validate_and_save(activity)' returns false, it needs # to bubble up to the 'import' method so we can stop execution return false unless create_activity(trackable, xml_activity) end end
create_activity(trackable, xml_activity)
click to toggle source
# File lib/dradis/plugins/projects/upload/v1/template.rb, line 55 def create_activity(trackable, xml_activity) activity = trackable.activities.new( action: xml_activity.at_xpath("action").text, created_at: Time.at(xml_activity.at_xpath("created_at").text.to_i) ) activity.project_id = project.id if activity.respond_to?(:project) set_activity_user(activity, xml_activity.at_xpath("user_email").text) validate_and_save(activity) end
create_comments(commentable, xml_comments)
click to toggle source
No-op here, overwritten in V2
# File lib/dradis/plugins/projects/upload/v1/template.rb, line 43 def create_comments(commentable, xml_comments) true end
create_issue(issue, xml_issue)
click to toggle source
# File lib/dradis/plugins/projects/upload/v1/template.rb, line 68 def create_issue(issue, xml_issue) # TODO: Need to find some way of checking for dups # May be combination of text, category_id and created_at issue.author = xml_issue.at_xpath('author').text.strip issue.text = xml_issue.at_xpath('text').text issue.node = project.issue_library issue.category = Category.issue return false unless validate_and_save(issue) return false unless create_activities(issue, xml_issue) return false unless create_comments(issue, xml_issue.xpath('comments/comment')) true end
finalize(template)
click to toggle source
# File lib/dradis/plugins/projects/upload/v1/template.rb, line 85 def finalize(template) logger.info { 'Wrapping up...' } finalize_nodes() finalize_evidence() finalize_attachments() logger.info { 'Done.' } end
finalize_attachments()
click to toggle source
# File lib/dradis/plugins/projects/upload/v1/template.rb, line 95 def finalize_attachments # Adjust attachment URLs for new Node IDs pending_changes[:attachment_notes].each do |item| text_attr = if defined?(ContentBlock) && item.is_a?(ContentBlock) :content else :text end logger.info { "Adjusting screenshot URLs: #{item.class.name} ##{item.id}" } new_text = item.send(text_attr).gsub(ATTACHMENT_URL) do |_| "!%s/projects/%d/nodes/%d/attachments/%s!" % [$1, project.id, lookup_table[:nodes][$2], $3] end item.send(text_attr.to_s + "=", new_text) raise "Couldn't save note attachment URL for #{item.class.name} ##{item.id}" unless validate_and_save(item) end end
finalize_evidence()
click to toggle source
Save the Evidence instance to the DB now that we have populated the original issues.
# File lib/dradis/plugins/projects/upload/v1/template.rb, line 118 def finalize_evidence pending_changes[:evidence].each_with_index do |evidence, i| logger.info { "Setting issue_id for evidence" } evidence.issue_id = lookup_table[:issues][evidence.issue_id.to_s] new_content = evidence.content.gsub(ATTACHMENT_URL) do |_| "!%s/projects/%d/nodes/%d/attachments/%s!" % [$1, project.id, lookup_table[:nodes][$2], $3] end evidence.content = new_content raise "Couldn't save Evidence :issue_id / attachment URL Evidence ##{evidence.id}" unless validate_and_save(evidence) pending_changes[:evidence_activity][i].each do |xml_activity| raise "Couldn't create activity for Evidence ##{evidence.id}" unless create_activity(evidence, xml_activity) end xml_comments = pending_changes[:evidence_comments][i] raise "Couldn't create comments for Evidence ##{evidence.id}" unless create_comments(evidence, xml_comments) end end
finalize_nodes()
click to toggle source
Fix relationships between nodes to ensure parents and childrens match with the new assigned :ids
# File lib/dradis/plugins/projects/upload/v1/template.rb, line 141 def finalize_nodes pending_changes[:orphan_nodes].each do |node| logger.info { "Finding parent for orphaned node: #{node.label}. Former parent was #{node.parent_id}" } node.parent_id = lookup_table[:nodes][node.parent_id.to_s] raise "Couldn't save node parent for Node ##{node.id}" unless validate_and_save(node) end end
parse_categories(template)
click to toggle source
Go through the categories, keep a translation table between the old category id and the new ones so we know to which category we should assign our notes
# File lib/dradis/plugins/projects/upload/v1/template.rb, line 152 def parse_categories(template) logger.info { 'Processing Categories...' } template.xpath('dradis-template/categories/category').each do |xml_category| old_id = xml_category.at_xpath('id').text.strip name = xml_category.at_xpath('name').text.strip category = nil # Prevent creating duplicate categories logger.info { "Looking for category: #{name}" } category = Category.find_or_create_by!(name: name) lookup_table[:categories][old_id] = category.id end logger.info { 'Done.' } end
parse_issues(template)
click to toggle source
Go through the issues, keep a translation table between the old issue id and the new ones. This is important for importing evidence Will need to adjust node ID after generating node structure
# File lib/dradis/plugins/projects/upload/v1/template.rb, line 172 def parse_issues(template) issue = nil logger.info { 'Processing Issues...' } template.xpath('dradis-template/issues/issue').each do |xml_issue| issue = Issue.new return false unless create_issue(issue, xml_issue) if issue.text =~ %r{^!(.*)/nodes/(\d+)/attachments/(.+)!$} pending_changes[:attachment_notes] << issue end old_id = xml_issue.at_xpath('id').text.strip lookup_table[:issues][old_id] = issue.id logger.info{ "New issue detected: #{issue.title}" } end logger.info { 'Done.' } end
parse_methodologies(template)
click to toggle source
# File lib/dradis/plugins/projects/upload/v1/template.rb, line 195 def parse_methodologies(template) methodology_category = Category.default methodology_library = project.methodology_library logger.info { 'Processing Methodologies...' } template.xpath('dradis-template/methodologies/methodology').each do |xml_methodology| # FIXME: this is wrong in a few levels, we should be able to save a # Methodology instance calling .save() but the current implementation # of the model would consider this a 'methodology template' and not an # instance. # # Also, methodology notes don't have a valid author, see # MethodologiesController#create action (i.e. 'methodology builder' is # used). Note.create!( author: 'methodology importer', node_id: methodology_library.id, category_id: methodology_category.id, text: xml_methodology.at_xpath('text').text ) end logger.info { 'Done.' } end
parse_node(xml_node)
click to toggle source
# File lib/dradis/plugins/projects/upload/v1/template.rb, line 221 def parse_node(xml_node) element = xml_node.at_xpath('type-id') type_id = element.text.nil? ? nil : element.text.strip label = xml_node.at_xpath('label').text.strip element = xml_node.at_xpath('parent-id') parent_id = element.text.blank? ? nil : element.text.strip # Node positions element = xml_node.at_xpath('position') position = (element && !element.text.nil?) ? element.text.strip : nil # Node properties element = xml_node.at_xpath('properties') properties = (element && !element.text.blank?) ? element.text.strip : nil created_at = xml_node.at_xpath('created-at') updated_at = xml_node.at_xpath('updated-at') logger.info { "New node detected: #{label}, parent_id: #{parent_id}, type_id: #{type_id}" } # There are exceptions to the rule, when it does not make sense to have # more than one of this nodes, in any given tree: # - the Configuration.uploadsNode node (detected by its label) # - any nodes with type different from DEFAULT or HOST if label == Configuration.plugin_uploads_node node = project.nodes.create_with(type_id: type_id, parent_id: parent_id) .find_or_create_by!(label: label) elsif Node::Types::USER_TYPES.exclude?(type_id.to_i) node = project.nodes.create_with(label: label) .find_or_create_by!(type_id: type_id) else # We don't want to validate child nodes here yet since they always # have invalid parent id's. They'll eventually be validated in the # finalize_nodes method. has_nil_parent = !parent_id node = project.nodes.new( type_id: type_id, label: label, parent_id: parent_id, position: position ) node.save!(validate: has_nil_parent) pending_changes[:orphan_nodes] << node if parent_id end if properties node.raw_properties = properties node.save!(validate: has_nil_parent) end node.update_attribute(:created_at, created_at.text.strip) if created_at node.update_attribute(:updated_at, updated_at.text.strip) if updated_at raise "Couldn't create activities for Node ##{node.id}" unless create_activities(node, xml_node) parse_node_notes(node, xml_node) parse_node_evidence(node, xml_node) node end
parse_node_evidence(node, xml_node)
click to toggle source
Create array of evidence from xml input. Cannot store in DB until we have a new issue id
# File lib/dradis/plugins/projects/upload/v1/template.rb, line 300 def parse_node_evidence(node, xml_node) xml_node.xpath('evidence/evidence').each do |xml_evidence| if xml_evidence.at_xpath('author') != nil created_at = xml_evidence.at_xpath('created-at') updated_at = xml_evidence.at_xpath('updated-at') evidence = Evidence.new( author: xml_evidence.at_xpath('author').text.strip, node_id: node.id, content: xml_evidence.at_xpath('content').text, issue_id: xml_evidence.at_xpath('issue-id').text.strip ) evidence.update_attribute(:created_at, created_at.text.strip) if created_at evidence.update_attribute(:updated_at, updated_at.text.strip) if updated_at pending_changes[:evidence] << evidence pending_changes[:evidence_activity] << xml_evidence.xpath('activities/activity') pending_changes[:evidence_comments] << xml_evidence.xpath('comments/comment') logger.info { "\tNew evidence added." } end end end
parse_node_notes(node, xml_node)
click to toggle source
# File lib/dradis/plugins/projects/upload/v1/template.rb, line 325 def parse_node_notes(node, xml_node) xml_node.xpath('notes/note').each do |xml_note| if xml_note.at_xpath('author') != nil old_id = xml_note.at_xpath('category-id').text.strip new_id = lookup_table[:categories][old_id] created_at = xml_note.at_xpath('created-at') updated_at = xml_note.at_xpath('updated-at') logger.info { "Note category rewrite, used to be #{old_id}, now is #{new_id}" } note = Note.create!( author: xml_note.at_xpath('author').text.strip, node_id: node.id, category_id: new_id, text: xml_note.at_xpath('text').text ) note.update_attribute(:created_at, created_at.text.strip) if created_at note.update_attribute(:updated_at, updated_at.text.strip) if updated_at raise "Couldn't save Note" unless validate_and_save(note) if note.text =~ %r{^!(.*)/nodes/(\d+)/attachments/(.+)!$} pending_changes[:attachment_notes] << note end raise "Couldn't create activities for Note ##{note.id}" unless create_activities(note, xml_note) raise "Couldn't create comments for Note ##{note.id}" unless create_comments(note, xml_note.xpath('comments/comment')) logger.info { "\tNew note added." } end end end
parse_nodes(template)
click to toggle source
# File lib/dradis/plugins/projects/upload/v1/template.rb, line 283 def parse_nodes(template) logger.info { 'Processing Nodes...' } # Re generate the Node tree structure template.xpath('dradis-template/nodes/node').each do |xml_node| node = parse_node(xml_node) # keep track of reassigned ids lookup_table[:nodes][xml_node.at_xpath('id').text.strip] = node.id end logger.info { 'Done.' } end
parse_report_content(template)
click to toggle source
# File lib/dradis/plugins/projects/upload/v1/template.rb, line 360 def parse_report_content(template); end
set_activity_user(activity, email)
click to toggle source
# File lib/dradis/plugins/projects/upload/v1/template.rb, line 389 def set_activity_user(activity, email) if Activity.column_names.include?('user') activity.user = email else activity.user_id = user_id_for_email(email) end end
user_id_for_email(email)
click to toggle source
# File lib/dradis/plugins/projects/upload/v1/template.rb, line 397 def user_id_for_email(email) users[email] || @default_user_id end
validate_and_save(instance)
click to toggle source
# File lib/dradis/plugins/projects/upload/v1/template.rb, line 410 def validate_and_save(instance) if instance.save return true else @logger.info{ "Malformed #{ instance.class.name } detected: #{ instance.errors.full_messages }" } return false end end