dradis-plugins 3.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +2 -0
  4. data/CHANGELOG.md +77 -0
  5. data/CONTRIBUTING.md +3 -0
  6. data/Gemfile +10 -0
  7. data/LICENSE +339 -0
  8. data/README.md +31 -0
  9. data/Rakefile +2 -0
  10. data/app/controllers/concerns/dradis/plugins/persistent_permissions.rb +43 -0
  11. data/app/controllers/dradis/plugins/export/base_controller.rb +18 -0
  12. data/dradis-plugins.gemspec +31 -0
  13. data/lib/dradis-plugins.rb +1 -0
  14. data/lib/dradis/plugins.rb +80 -0
  15. data/lib/dradis/plugins/base.rb +45 -0
  16. data/lib/dradis/plugins/configurable.rb +26 -0
  17. data/lib/dradis/plugins/content_service/base.rb +26 -0
  18. data/lib/dradis/plugins/content_service/boards.rb +32 -0
  19. data/lib/dradis/plugins/content_service/categories.rb +9 -0
  20. data/lib/dradis/plugins/content_service/content_blocks.rb +44 -0
  21. data/lib/dradis/plugins/content_service/core.rb +53 -0
  22. data/lib/dradis/plugins/content_service/evidence.rb +36 -0
  23. data/lib/dradis/plugins/content_service/issues.rb +94 -0
  24. data/lib/dradis/plugins/content_service/nodes.rb +88 -0
  25. data/lib/dradis/plugins/content_service/notes.rb +43 -0
  26. data/lib/dradis/plugins/content_service/properties.rb +9 -0
  27. data/lib/dradis/plugins/engine.rb +15 -0
  28. data/lib/dradis/plugins/export.rb +1 -0
  29. data/lib/dradis/plugins/export/base.rb +57 -0
  30. data/lib/dradis/plugins/gem_version.rb +17 -0
  31. data/lib/dradis/plugins/import.rb +3 -0
  32. data/lib/dradis/plugins/import/filters.rb +51 -0
  33. data/lib/dradis/plugins/import/filters/base.rb +16 -0
  34. data/lib/dradis/plugins/import/result.rb +12 -0
  35. data/lib/dradis/plugins/settings.rb +91 -0
  36. data/lib/dradis/plugins/template_service.rb +104 -0
  37. data/lib/dradis/plugins/templates.rb +57 -0
  38. data/lib/dradis/plugins/thor.rb +30 -0
  39. data/lib/dradis/plugins/thor_helper.rb +29 -0
  40. data/lib/dradis/plugins/upload.rb +10 -0
  41. data/lib/dradis/plugins/upload/base.rb +57 -0
  42. data/lib/dradis/plugins/upload/field_processor.rb +35 -0
  43. data/lib/dradis/plugins/upload/importer.rb +78 -0
  44. data/lib/dradis/plugins/version.rb +11 -0
  45. data/spec/internal/log/test.log +0 -0
  46. data/spec/lib/dradis/plugins/content_service/boards_spec.rb +45 -0
  47. data/spec/lib/dradis/plugins/content_service/issues_spec.rb +64 -0
  48. data/spec/settings_spec.rb +88 -0
  49. data/spec/spec_helper.rb +2 -0
  50. metadata +138 -0
@@ -0,0 +1,36 @@
1
+ module Dradis::Plugins::ContentService
2
+ module Evidence
3
+ extend ActiveSupport::Concern
4
+
5
+ def create_evidence(args={})
6
+ content = args.fetch(:content, default_evidence_content)
7
+ node = args.fetch(:node, default_node_parent)
8
+ issue = args[:issue] || default_evidence_issue
9
+
10
+ evidence = node.evidence.new(issue_id: issue.id, content: content)
11
+
12
+ if evidence.valid?
13
+ evidence.save
14
+ else
15
+ try_rescue_from_length_validation(
16
+ model: evidence,
17
+ field: :content,
18
+ text: content,
19
+ msg: 'Error in create_evidence()'
20
+ )
21
+ end
22
+
23
+ evidence
24
+ end
25
+
26
+ private
27
+
28
+ def default_evidence_content
29
+ "create_evidence() invoked by #{plugin} without a :content parameter"
30
+ end
31
+
32
+ def default_evidence_issue
33
+ create_issue(text: "#[Title]#\nAuto-generated issue.\n\n#[Description]#\ncreate_evidence() invoked by #{plugin} without an :issue parameter")
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,94 @@
1
+ module Dradis::Plugins::ContentService
2
+ module Issues
3
+ extend ActiveSupport::Concern
4
+
5
+ def all_issues
6
+ project.issues.where(category_id: default_issue_category.id)
7
+ end
8
+
9
+ def create_issue(args={})
10
+ text = args.fetch(:text, default_issue_text)
11
+ # NOTE that ID is the unique issue identifier assigned by the plugin,
12
+ # and is not to be confused with the Issue#id primary key
13
+ id = args.fetch(:id, default_issue_id)
14
+
15
+ # Bail if we already have this issue in the cache
16
+ uuid = [plugin::Engine::plugin_name, id]
17
+ cache_key = uuid.join('-')
18
+
19
+ return issue_cache[cache_key] if issue_cache.key?(cache_key)
20
+
21
+ # we inject the source Plugin and unique ID into the issue's text
22
+ plugin_details =
23
+ "\n\n#[plugin]#\n#{uuid[0]}\n" \
24
+ "\n\n#[plugin_id]#\n#{uuid[1]}\n"
25
+ text << plugin_details
26
+
27
+ issue = Issue.new(text: text) do |i|
28
+ i.author = default_author
29
+ i.node = project.issue_library
30
+ i.category = default_issue_category
31
+ end
32
+
33
+ if issue.valid?
34
+ issue.save
35
+ else
36
+ try_rescue_from_length_validation(
37
+ model: issue,
38
+ field: :text,
39
+ text: text,
40
+ msg: 'Error in create_issue()',
41
+ tail: plugin_details
42
+ )
43
+ end
44
+
45
+ issue_cache.store(cache_key, issue)
46
+ end
47
+
48
+ # Create a hash with all issues where the keys correspond to the field passed
49
+ # as an argument.
50
+ #
51
+ # This is use by the plugins to check whether a given issue is already in
52
+ # the project.
53
+ # def all_issues_by_field(field)
54
+ # # we don't memoize it because we want it to reflect recently added Issues
55
+ # klass = class_for(:issue)
56
+ #
57
+ # issues_map = klass.where(category_id: default_issue_category.id).map do |issue|
58
+ # [issue.fields[field], issue]
59
+ # end
60
+ # Hash[issues_map]
61
+ # end
62
+
63
+ # Accesing the library by primary sorting key. Raise an exception unless
64
+ # the issue library cache has been initialized.
65
+ def issue_cache
66
+ @issue_cache ||= begin
67
+ issues_map = all_issues.map do |issue|
68
+ cache_key = [
69
+ issue.fields['plugin'],
70
+ issue.fields['plugin_id']
71
+ ].join('-')
72
+
73
+ [cache_key, issue]
74
+ end
75
+ Hash[issues_map]
76
+ end
77
+ end
78
+
79
+
80
+ private
81
+
82
+ def default_issue_category
83
+ @default_issue_category ||= Category.issue
84
+ end
85
+
86
+ def default_issue_id
87
+ "create_issue() invoked by #{plugin} without an :id parameter"
88
+ end
89
+
90
+ def default_issue_text
91
+ "create_issue() invoked by #{plugin} without a :text parameter"
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,88 @@
1
+ module Dradis::Plugins::ContentService
2
+ module Nodes
3
+ extend ActiveSupport::Concern
4
+
5
+ def reporting_nodes
6
+ nodes = []
7
+
8
+ nodes |= nodes_from_evidence
9
+ nodes |= nodes_from_properties
10
+
11
+ # Note that the below sorting would the non-IP nodes first, then the IP
12
+ # nodes, and will sort them by each octet.
13
+ #
14
+ # See:
15
+ # http://stackoverflow.com/questions/13996033/sorting-an-array-in-ruby-special-case
16
+ # http://tech.maynurd.com/archives/124
17
+ nodes.sort_by! { |node| node.label.split('.').map(&:to_i) }
18
+ end
19
+
20
+ def create_node(args={})
21
+ label = args[:label] || default_node_label
22
+ parent = args[:parent] || default_node_parent
23
+
24
+ type_id = begin
25
+ if args[:type]
26
+ tmp_type = args[:type].to_s.upcase
27
+ if Node::Types::const_defined?(tmp_type)
28
+ "Node::Types::#{tmp_type}".constantize
29
+ else
30
+ default_node_type
31
+ end
32
+ else
33
+ default_node_type
34
+ end
35
+ end
36
+
37
+ new_node = parent.children.find_or_initialize_by(
38
+ label: label,
39
+ type_id: type_id
40
+ )
41
+ # `Node#project_id` method does not exist in CE. We set the project for
42
+ # `new_node` once it is initialized using `Node#project=`
43
+ new_node.project = parent.project
44
+ new_node.save
45
+ new_node
46
+ end
47
+
48
+ private
49
+
50
+ def default_node_label
51
+ "create_node() invoked by #{plugin} without a :label parameter"
52
+ end
53
+
54
+ def default_node_parent
55
+ @default_parent_node ||= project.plugin_parent_node
56
+ end
57
+
58
+ def default_node_type
59
+ @default_node_type ||= Node::Types::DEFAULT
60
+ end
61
+
62
+
63
+ # Private: this method returns a list of nodes associated with Evidence
64
+ # instances. When a node is affected by multiple issues, or multiple pieces
65
+ # of evidence, we just want a single reference to it.
66
+ #
67
+ # Returns and Array with a unique collection of Nodes.
68
+ def nodes_from_evidence
69
+ all_issues.
70
+ includes(:evidence, evidence: :node).
71
+ collect(&:evidence).
72
+ # Each Issue can have 0, 1 or more Evidence
73
+ map { |evidence_collection| evidence_collection.collect(&:node) }.
74
+ flatten.
75
+ uniq
76
+ end
77
+
78
+ # Private: this method returns a list of nodes in the project that have some
79
+ # properties associated with them. Typically properties are used for HOST
80
+ # type nodes, and even if they have no issues / evidence associated, we want
81
+ # to include them in the report.
82
+ #
83
+ # Returns and Array with a unique collection of Nodes.
84
+ def nodes_from_properties
85
+ project.nodes.user_nodes.where('properties IS NOT NULL AND properties != \'{}\'')
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,43 @@
1
+ module Dradis::Plugins::ContentService
2
+ module Notes
3
+ extend ActiveSupport::Concern
4
+
5
+ def all_notes
6
+ project.notes.where(category: Category.report)
7
+ end
8
+
9
+ def create_note(args={})
10
+ cat = args.fetch(:category, default_note_category)
11
+ node = args.fetch(:node, default_node_parent)
12
+ text = args.fetch(:text, default_note_text)
13
+
14
+ note = node.notes.new(
15
+ text: text,
16
+ category: cat,
17
+ author: default_author
18
+ )
19
+
20
+ if note.valid?
21
+ note.save
22
+ else
23
+ try_rescue_from_length_validation(
24
+ model: note,
25
+ field: :text,
26
+ text: text,
27
+ msg: 'Error in create_note()'
28
+ )
29
+ end
30
+
31
+ note
32
+ end
33
+
34
+ private
35
+ def default_note_category
36
+ @default_note_category ||= Category.default
37
+ end
38
+
39
+ def default_note_text
40
+ "create_note() invoked by #{plugin} without a :text parameter"
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,9 @@
1
+ module Dradis::Plugins::ContentService
2
+ module Properties
3
+ extend ActiveSupport::Concern
4
+
5
+ def all_properties
6
+ project.content_library.properties
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ module Dradis
2
+ module Plugins
3
+ class Engine < ::Rails::Engine
4
+ isolate_namespace Dradis::Plugins
5
+
6
+ config.dradis = ActiveSupport::OrderedOptions.new
7
+
8
+ initializer "dradis-plugins.set_configs" do |app|
9
+ options = app.config.dradis
10
+ options.base_export_controller_class_name ||= 'AuthenticatedController'
11
+ options.thor_helper_module ||= Dradis::Plugins::ThorHelper
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1 @@
1
+ require 'dradis/plugins/export/base'
@@ -0,0 +1,57 @@
1
+ # This module contains basic Export plugin functions.
2
+ #
3
+
4
+ module Dradis
5
+ module Plugins
6
+ module Export
7
+ class Base
8
+ attr_accessor :content_service, :logger, :options, :plugin, :project
9
+
10
+ def initialize(args={})
11
+ # Save everything just in case the implementing class needs any of it.
12
+ @options = args
13
+
14
+ # Can't use :fetch for :plugin or :default_plugin gets evaluated
15
+ @logger = args.fetch(:logger, Rails.logger)
16
+ @plugin = args[:plugin] || default_plugin
17
+ @project = args.key?(:project_id) ? Project.find(args[:project_id]) : nil
18
+
19
+ @content_service = args.fetch(:content_service, default_content_service)
20
+
21
+ post_initialize(args)
22
+ end
23
+
24
+ def export(args={})
25
+ raise "The export() method is not implemented in this plugin [#{self.class.name}]."
26
+ end
27
+
28
+ # This method can be overwriten by plugins to do initialization tasks.
29
+ def post_initialize(args={})
30
+ end
31
+
32
+ private
33
+ def default_content_service
34
+ @content ||= Dradis::Plugins::ContentService::Base.new(
35
+ logger: logger,
36
+ plugin: plugin,
37
+ project: project
38
+ )
39
+ end
40
+
41
+ # This assumes the plugin's Exporter class is directly nexted into the
42
+ # plugin's namespace (e.g. Dradis::Plugins::HtmlExport::Exporter)
43
+ def default_plugin
44
+ plugin_module = self.class.name.deconstantize
45
+ plugin_constant = plugin_module.constantize
46
+ plugin_engine = plugin_constant::Engine
47
+ if Dradis::Plugins.registered?(plugin_engine)
48
+ plugin_constant
49
+ else
50
+ raise "You need to pass a :plugin value to your Exporter or define it under your plugin's root namespace."
51
+ end
52
+ end
53
+
54
+ end # Base
55
+ end # Export
56
+ end # Plugins
57
+ end # Core
@@ -0,0 +1,17 @@
1
+ module Dradis
2
+ module Plugins
3
+ # Returns the version of the currently loaded Frontend as a <tt>Gem::Version</tt>
4
+ def self.gem_version
5
+ Gem::Version.new VERSION::STRING
6
+ end
7
+
8
+ module VERSION
9
+ MAJOR = 3
10
+ MINOR = 18
11
+ TINY = 0
12
+ PRE = nil
13
+
14
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ require 'dradis/plugins/import/filters'
2
+ require 'dradis/plugins/import/filters/base'
3
+ require 'dradis/plugins/import/result'
@@ -0,0 +1,51 @@
1
+ module Dradis
2
+ module Plugins
3
+ module Import
4
+
5
+ module Filters
6
+ class << self
7
+ # -- Class Methods --------------------------------------------------------
8
+ # One Import plugin can define several filters (e.g. to query different
9
+ # endpoints of a remote API).
10
+ #
11
+ # Use this method in your Importer to register different filters, pass a
12
+ # block or a class.
13
+ #
14
+ # Examples:
15
+ #
16
+ # register_filter :by_osvdb_id do
17
+ # def c
18
+ # end
19
+ def add(plugin, label, filter, &block)
20
+ filter ||= Class.new(Dradis::Plugins::Import::Filters::Base)
21
+ filter.class_eval(&block) if block_given?
22
+
23
+ unless filter.method_defined?(:query)
24
+ raise NoMethodError, "query() is not declared in the #{label.inspect} filter"
25
+ end
26
+
27
+ base = Dradis::Plugins::Import::Filters::Base
28
+ unless filter.ancestors.include?(base)
29
+ raise "#{label.inspect} is not a #{base}"
30
+ end
31
+
32
+ _filters[plugin] = {} unless _filters.key?(plugin)
33
+ _filters[plugin][label] = filter
34
+ end
35
+
36
+ # Provides access to filters by plugin
37
+ # :api: public
38
+ def [](plugin)
39
+ _filters[plugin]
40
+ end
41
+
42
+ # :api: private
43
+ def _filters
44
+ @filters ||= {}
45
+ end
46
+ end
47
+ end
48
+
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,16 @@
1
+ module Dradis
2
+ module Plugins
3
+ module Import
4
+ module Filters
5
+
6
+ class Base
7
+ def self.query(args={})
8
+ instance = self.new
9
+ instance.query(args)
10
+ end
11
+ end
12
+
13
+ end
14
+ end
15
+ end
16
+ end