dradis-plugins 3.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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