dradis-plugins 3.0.0 → 3.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ad5a0f1011a150397c1378acfe17ae79dfcc7118
4
- data.tar.gz: 5a9b942df578368054a859030a42ab5e752d2369
3
+ metadata.gz: 74496768f4fd445889de85903ef1470ea133c87f
4
+ data.tar.gz: 71a03d2ef7b24237a75f87b3ee9295afb963b0ec
5
5
  SHA512:
6
- metadata.gz: c030f8c2e6a87f50a053100cf4f11343ff88958a719b7c81cabc8c8ec907cbb627df25a2b6c6aa890d1137fe27eb1c1787b1002425d24a0a71bd3efee83064ec
7
- data.tar.gz: 58508d65a5d4cdd27c1bfd6263929533627569355f3a1185e28503e782f0ac3cd640d8e1e76f30f86047f7c3bc07feb9a22ccda8c3e9584e3ad87f00e87e7cdf
6
+ metadata.gz: 8f1d927b2727a5e010fcd889a37fc858520b309ab89decf49d4c32d222fe60a0654a05b9a5ad5a7f9d2d14616cf0383c8827325f100272bc8b30834354f42b35
7
+ data.tar.gz: 48f722e8962d1f4a07e26b5d975fe2b98097c7b666c0255e6424ceafc67f8f635c7925719b55d41c792265347cfe7afd190eb960bff84da3c69d5fd6d88bcba8
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ -f d
data/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # Plugin manager for the Dradis Framework
2
2
 
3
- [![Build Status](https://secure.travis-ci.org/dradis/dradis-plugins.png?branch=master)](http://travis-ci.org/dradis/dradis-plugins)
3
+ [![Build Status](https://secure.travis-ci.org/dradis/dradis-plugins.png?branch=master)](http://travis-ci.org/dradis/dradis-plugins) [![Code Climate](https://codeclimate.com/github/dradis/dradis-plugins.png)](https://codeclimate.com/github/dradis/dradis-plugins.png)
4
4
 
5
5
  This gem contains the base classes needed to manage the plugins in Dradis.
6
6
 
7
7
  The Dradis 3 gemified plugin Engines need to include Dradis::Plugins::Base which is defined in this class.
8
8
 
9
- Warning, we me end up merging this gem with Dradis::Core!!
9
+ Warning, we may end up merging this gem with Dradis::Core!!
10
10
 
11
11
  The plugin requires Dradis 3.0 or higher.
12
12
 
@@ -22,4 +22,4 @@ See the Dradis Framework's [CONTRIBUTING.md](https://github.com/dradis/dradisfra
22
22
 
23
23
  ## License
24
24
 
25
- Dradis Framework and all its components are released under [GNU General Public License version 2.0](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html) as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.
25
+ Dradis Framework and all its components are released under [GNU General Public License version 2.0](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html) as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.
@@ -0,0 +1,9 @@
1
+ module Dradis
2
+ module Plugins
3
+ module Export
4
+ class BaseController < Dradis::Plugins::base_export_controller_class.to_s.constantize
5
+
6
+ end
7
+ end
8
+ end
9
+ end
@@ -22,6 +22,7 @@ Gem::Specification.new do |spec|
22
22
 
23
23
  spec.add_development_dependency 'bundler', '~> 1.6'
24
24
  spec.add_development_dependency 'rake', '~> 10.0'
25
+ spec.add_development_dependency 'rspec-rails'
25
26
 
26
27
  # By not including Rails as a dependency, we can use the gem with different
27
28
  # versions of Rails (a sure recipe for disaster, I'm sure), which is needed
@@ -1,53 +1,11 @@
1
- require 'dradis/plugins/engine'
2
- require 'dradis/plugins/version'
3
-
4
- require 'dradis/plugins/content_service'
5
- require 'dradis/plugins/template_service'
6
-
7
- require 'dradis/plugins/export'
8
- require 'dradis/plugins/import'
9
- require 'dradis/plugins/upload'
10
-
11
- require 'dradis/plugins/templates'
12
-
13
1
  module Dradis
14
2
  module Plugins
15
- module Base
16
- def self.included(base)
17
- base.extend ClassMethods
18
- base.class_eval do
19
- # mattr_accessor :plugin_name
20
-
21
- @features = []
22
- @name = 'Use plugin_info(args) with :name to provide a name for this plugin.'
23
- Plugins::register(self)
24
- end
25
-
26
- # Extend the engine with other functionality
27
- base.send :include, Dradis::Plugins::Templates
28
- end
29
-
30
- module ClassMethods
31
- def description(new_description)
32
- @description = new_description
33
- end
34
-
35
- def plugin_description
36
- @description ||= "This plugin doesn't provide a :description"
37
- end
3
+ mattr_accessor :configuration_class
4
+ mattr_accessor :base_export_controller_class
5
+ mattr_accessor :thor_helper_module
38
6
 
39
- def plugin_name
40
- self.name.split('::')[2].underscore.to_sym
41
- end
42
-
43
- def provides(*list)
44
- @features = list
45
- end
46
-
47
- def provides?(feature)
48
- @features.include?(feature)
49
- end
50
- end
7
+ def self.setup(&block)
8
+ yield self
51
9
  end
52
10
 
53
11
  class << self
@@ -108,4 +66,23 @@ module Dradis
108
66
  end
109
67
  end
110
68
  end
111
- end
69
+ end
70
+
71
+
72
+ require 'dradis/plugins/engine'
73
+ require 'dradis/plugins/version'
74
+
75
+ require 'dradis/plugins/content_service'
76
+ require 'dradis/plugins/template_service'
77
+
78
+ require 'dradis/plugins/base'
79
+ require 'dradis/plugins/export'
80
+ require 'dradis/plugins/import'
81
+ require 'dradis/plugins/upload'
82
+
83
+ # Common functionality
84
+ require 'dradis/plugins/configurable'
85
+ require 'dradis/plugins/settings'
86
+ require 'dradis/plugins/templates'
87
+ require 'dradis/plugins/thor'
88
+ require 'dradis/plugins/thor_helper'
@@ -0,0 +1,45 @@
1
+ module Dradis
2
+ module Plugins
3
+ module Base
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ # mattr_accessor :plugin_name
8
+
9
+ @features = []
10
+ @name = 'Use plugin_info(args) with :name to provide a name for this plugin.'
11
+ Plugins::register(self)
12
+
13
+ # Extend the engine with other functionality
14
+ include Dradis::Plugins::Configurable
15
+ include Dradis::Plugins::Templates
16
+ include Dradis::Plugins::Thor
17
+ end
18
+
19
+ module ClassMethods
20
+ def description(new_description)
21
+ @description = new_description
22
+ end
23
+
24
+ def plugin_description
25
+ @description ||= "This plugin doesn't provide a :description"
26
+ end
27
+
28
+ def plugin_name
29
+ @plugin_name ||= self.name.split('::')[-2].underscore.to_sym
30
+ end
31
+
32
+ def provides(*list)
33
+ @features = list
34
+ if list.include?(:upload)
35
+ include Dradis::Plugins::Upload::Base
36
+ end
37
+ end
38
+
39
+ def provides?(feature)
40
+ @features.include?(feature)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,26 @@
1
+ module Dradis::Plugins
2
+ module Configurable
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ delegate :settings, to: :instance
7
+
8
+ def settings_namespace
9
+ @settings_namespace || plugin_name
10
+ end
11
+
12
+ def addon_settings(namespace = nil, &block)
13
+ @settings_namespace = namespace if namespace
14
+ yield self if block_given?
15
+ end
16
+
17
+ def instance
18
+ @instance ||= new
19
+ end
20
+ end
21
+
22
+ def settings
23
+ @settings ||= Dradis::Plugins::Settings.new(self.class.settings_namespace)
24
+ end
25
+ end
26
+ end
@@ -3,8 +3,21 @@ module Dradis
3
3
  class ContentService
4
4
  attr_accessor :logger, :plugin
5
5
 
6
+ # ----------------------------------------------------------- Initializer
7
+ #
8
+
9
+ # @option plugin [Class] the 'wrapper' module of a plugin, e.g.
10
+ # Dradis::Plugins::Nessus
6
11
  def initialize(args={})
7
- @plugin = args[:plugin]
12
+ self.plugin = args.fetch(:plugin)
13
+ self.logger = args[:logger]
14
+ end
15
+
16
+
17
+ # ------------------------------------------------------ Query operations
18
+
19
+ def all_issues
20
+ class_for(:issue).where(category_id: default_issue_category.id)
8
21
  end
9
22
 
10
23
  # Create a hash with all issues where the keys correspond to the field passed
@@ -12,26 +25,56 @@ module Dradis
12
25
  #
13
26
  # This is use by the plugins to check whether a given issue is already in
14
27
  # the project.
15
- def all_issues_by_field(field)
16
- # we don't memoize it because we want it to reflect recently added Issues
17
- klass = class_for(:issue)
18
-
19
- issues_map = klass.where(category_id: default_issue_category.id).map do |issue|
20
- [issue.fields[field], issue]
28
+ # def all_issues_by_field(field)
29
+ # # we don't memoize it because we want it to reflect recently added Issues
30
+ # klass = class_for(:issue)
31
+ #
32
+ # issues_map = klass.where(category_id: default_issue_category.id).map do |issue|
33
+ # [issue.fields[field], issue]
34
+ # end
35
+ # Hash[issues_map]
36
+ # end
37
+
38
+ # Accesing the library by primary sorting key. Raise an exception unless
39
+ # the issue library cache has been initialized.
40
+ def issue_cache
41
+ @issue_cache ||= begin
42
+ klass = class_for(:issue)
43
+
44
+ issues_map = klass.where(category_id: default_issue_category.id).map do |issue|
45
+ cache_key = [
46
+ issue.fields['plugin'],
47
+ issue.fields['plugin_id']
48
+ ].join('-')
49
+
50
+ [cache_key, issue]
51
+ end
52
+ Hash[issues_map]
21
53
  end
22
- Hash[issues_map]
23
54
  end
24
55
 
56
+ def all_notes
57
+ class_for(:note).where(category_id: class_for(:category).report.id)
58
+ end
59
+
60
+ # ----------------------------------------------------- Create operations
61
+ #
25
62
 
26
63
  def create_node(args={})
27
- label = args[:label] || "create_node() invoked by #{plugin} without a :label parameter"
28
- parent = args[:parent] || default_parent_node
64
+ label = args[:label] || "create_node() invoked by #{plugin} without a :label parameter"
65
+ parent = args[:parent] || default_parent_node
66
+
29
67
  type_id = begin
30
- tmp_type = args[:type].to_s.upcase
31
- class_for(:node)::Types::const_defined?(tmp_type) ? "#{class_for(:node)::Types}::#{tmp_type}".constantize : default_node_type
68
+ if args[:type]
69
+ tmp_type = args[:type].to_s.upcase
70
+ class_for(:node)::Types::const_defined?(tmp_type) ? "#{class_for(:node)::Types}::#{tmp_type}".constantize : default_node_type
71
+ else
72
+ default_node_type
73
+ end
32
74
  end
33
75
 
34
76
  # FIXME: how ugly is this?
77
+ # UPGRADE: Rails 4 find_or_create_by_
35
78
  if Rails::VERSION::MAJOR == 3
36
79
  parent.children.find_or_create_by_label_and_type_id(label, type_id)
37
80
  else
@@ -43,20 +86,45 @@ module Dradis
43
86
  node = args[:node] || default_parent_node
44
87
  text = args[:text] || "create_note() invoked by #{plugin} without a :text parameter"
45
88
 
46
- node.notes.create text: text, category: default_note_category, author: default_author
89
+ note = node.notes.new(text: text, category: default_note_category, author: default_author)
90
+ if note.valid?
91
+ note.save
92
+ else
93
+ try_rescue_from_length_validation(model: note, field: :text, text: text, msg: 'Error in create_note()')
94
+ end
95
+
96
+ note
47
97
  end
48
98
 
49
99
  def create_issue(args={})
50
100
  text = args[:text] || "create_issue() invoked by #{plugin} without a :text parameter"
101
+ # NOTE that ID is the unique issue identifier assigned by the plugin,
102
+ # and is not to be confused with the Issue#id primary key
103
+ id = args[:id] || "create_issue() invoked by #{plugin} without an :id parameter"
104
+
105
+ # Bail if we already have this issue in the cache
106
+ uuid = [plugin::Engine::plugin_name, id]
107
+ cache_key = uuid.join('-')
108
+
109
+ return issue_cache[cache_key] if issue_cache.key?(cache_key)
51
110
 
52
- # we inject the source Plugin into the issue's text
53
- text << "\n\n#[plugin]#\n#{plugin::Engine::plugin_name}\n"
111
+ # we inject the source Plugin and unique ID into the issue's text
112
+ text << "\n\n#[plugin]#\n#{uuid[0]}\n"
113
+ text << "\n\n#[plugin_id]#\n#{uuid[1]}\n"
54
114
 
55
- class_for(:issue).create(text: text) do |i|
115
+ issue = class_for(:issue).new(text: text) do |i|
56
116
  i.author = default_author
57
117
  i.node = issuelib
58
118
  i.category = default_issue_category
59
119
  end
120
+
121
+ if issue.valid?
122
+ issue.save
123
+ else
124
+ try_rescue_from_length_validation(model: issue, field: :text, text: text, msg: 'Error in create_issue()')
125
+ end
126
+
127
+ issue_cache.store(cache_key, issue)
60
128
  end
61
129
 
62
130
  def create_evidence(args={})
@@ -64,15 +132,22 @@ module Dradis
64
132
  node = args[:node] || default_parent_node
65
133
  issue = args[:issue] || create_issue(text: "#[Title]#\nAuto-generated issue.\n\n#[Description]#\ncreate_evidence() invoked by #{plugin} without an :issue parameter")
66
134
 
67
- node.evidence.create(issue_id: issue.id, content: content)
135
+ evidence = node.evidence.new(issue_id: issue.id, content: content)
136
+ if evidence.valid?
137
+ evidence.save
138
+ else
139
+ try_rescue_from_length_validation(model: evidence, field: :content, text: content, msg: 'Error in create_evidence()')
140
+ end
141
+ evidence
68
142
  end
69
143
 
144
+
70
145
  private
146
+
71
147
  def class_for(model)
72
- "Dradis::Core::#{model.to_s.capitalize}".constantize
148
+ "::#{model.to_s.capitalize}".constantize
73
149
  end
74
150
 
75
-
76
151
  def default_author
77
152
  @default_author ||= "#{plugin::Engine.plugin_name.to_s.humanize} upload plugin"
78
153
  end
@@ -90,12 +165,37 @@ module Dradis
90
165
  end
91
166
 
92
167
  def default_parent_node
93
- @default_parent_node ||= class_for(:node).find_or_create_by(label: 'plugin.output')
168
+ @default_parent_node ||= class_for(:node).plugin_parent_node
94
169
  end
95
170
 
96
171
  def issuelib
97
172
  @issuelib ||= class_for(:node).issue_library
98
173
  end
174
+
175
+ def try_rescue_from_length_validation(args={})
176
+ model = args[:model]
177
+ field = args[:field]
178
+ text = args[:text]
179
+ msg = args[:msg]
180
+
181
+ logger.error{ "Trying to rescue from a :length error" }
182
+
183
+ if model.errors[field]
184
+ # the plugin tried to store too much information
185
+ msg = "#[Title]#\nTruncation warning!\n\n"
186
+ msg << "#[Error]#\np(alert alert-error). The plugin tried to store content that was too big for the DB. Review the source to ensure no important data was lost.\n\n"
187
+ msg << text
188
+ model.send("#{field}=", msg.truncate(65300))
189
+ else
190
+ # bail
191
+ msg = "#[Title]#\n#{msg}\n\n"
192
+ msg << "#[Description]#\nbc. #{issue.errors.inspect}\n\n"
193
+ model.send("#{field}=", msg)
194
+ end
195
+ if model.valid?
196
+ model.save
197
+ end
198
+ end
99
199
  end
100
200
  end
101
201
  end
@@ -14,6 +14,12 @@ module Dradis
14
14
  # initializer 'frontend.asset_precompile_paths' do |app|
15
15
  # app.config.assets.precompile += ["dradis/frontend/manifests/*"]
16
16
  # end
17
+
18
+ Dradis::Plugins::setup do |config|
19
+ config.base_export_controller_class = 'ProjectScopedController'
20
+ config.configuration_class = '::Configuration'
21
+ config.thor_helper_module = 'Dradis::Plugins::ThorHelper'
22
+ end
17
23
  end
18
24
  end
19
- end
25
+ end
@@ -5,11 +5,15 @@ module Dradis
5
5
  module Plugins
6
6
  module Export
7
7
  class Base
8
- attr_accessor :logger
8
+ attr_accessor :content_service, :logger
9
9
 
10
10
  def initialize(args={})
11
11
  @logger = args.fetch(:logger, Rails.logger)
12
12
 
13
+ @content_service = args[:content_service] || default_content_service
14
+
15
+ content_service.logger = logger
16
+
13
17
  post_initialize(args)
14
18
  end
15
19
 
@@ -20,8 +24,13 @@ module Dradis
20
24
  # This method can be overwriten by plugins to do initialization tasks.
21
25
  def post_initialize(args={})
22
26
  end
23
- end # Base
24
27
 
28
+ private
29
+ def default_content_service
30
+ @content ||= Dradis::Plugins::ContentService.new
31
+ end
32
+
33
+ end # Base
25
34
  end # Export
26
35
  end # Plugins
27
- end # Core
36
+ end # Core
@@ -7,11 +7,11 @@ module Dradis
7
7
 
8
8
  module VERSION
9
9
  MAJOR = 3
10
- MINOR = 0
11
- TINY = 0
12
- PRE = nil
10
+ MINOR = 5
11
+ TINY = 0
12
+ PRE = nil
13
13
 
14
14
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
15
15
  end
16
16
  end
17
- end
17
+ end
@@ -16,12 +16,12 @@ module Dradis
16
16
  # register_filter :by_osvdb_id do
17
17
  # def c
18
18
  # end
19
- def add(label, filter, &block)
19
+ def add(plugin, label, filter, &block)
20
20
  filter ||= Class.new(Dradis::Plugins::Import::Filters::Base)
21
21
  filter.class_eval(&block) if block_given?
22
22
 
23
23
  unless filter.method_defined?(:query)
24
- raise NoMethodError, "query() is not declared in the #{label.inspect} strategy"
24
+ raise NoMethodError, "query() is not declared in the #{label.inspect} filter"
25
25
  end
26
26
 
27
27
  base = Dradis::Plugins::Import::Filters::Base
@@ -29,13 +29,14 @@ module Dradis
29
29
  raise "#{label.inspect} is not a #{base}"
30
30
  end
31
31
 
32
- _filters[label] = filter
32
+ _filters[plugin] = {} unless _filters.key?(plugin)
33
+ _filters[plugin][label] = filter
33
34
  end
34
35
 
35
- # Provides access to strategies by label
36
+ # Provides access to filters by plugin
36
37
  # :api: public
37
- def [](label)
38
- _filters[label]
38
+ def [](plugin)
39
+ _filters[plugin]
39
40
  end
40
41
 
41
42
  # :api: private
@@ -1,11 +1,12 @@
1
1
  module Dradis::Plugins::Import
2
2
  class Result
3
- attr_accessor :description, :tags, :title
3
+ attr_accessor :description, :id, :tags, :title
4
4
 
5
5
  def initialize(args={})
6
6
  @description = args[:description] || "The Import plugin didn't provide a :description for this result."
7
- @tags = args[:tags] || []
8
- @title = args[:description] || "The Import plugin didn't provide a :title for this result."
7
+ @id = args[:id] || "The Import plugin didn't provide an :id for this result."
8
+ @tags = args[:tags] || []
9
+ @title = args[:title] || "The Import plugin didn't provide a :title for this result."
9
10
  end
10
11
  end
11
12
  end
@@ -0,0 +1,103 @@
1
+ module Dradis::Plugins
2
+ class Settings
3
+ attr_reader :namespace
4
+
5
+ def initialize(namespace)
6
+ @namespace = namespace
7
+ @dirty_options ||= {}
8
+ @default_options ||= HashWithIndifferentAccess.new
9
+ end
10
+
11
+ def respond_to?(name)
12
+ super || @dirty_options.key?(name.to_sym)
13
+ end
14
+
15
+ def all
16
+ @default_options.map do |key, value|
17
+ {
18
+ name: key.to_sym,
19
+ value: value = dirty_or_db_setting_or_default(key.to_sym),
20
+ default: is_default?(key, value)
21
+ }
22
+ end.sort_by{ |o| o[:name] }
23
+ end
24
+
25
+ def save
26
+ @dirty_options.reject{ |k, v| v.present? && v == db_setting(k) }.each{ |k, v| write_to_db(k, v) }
27
+ end
28
+
29
+ def update_settings(opts = {})
30
+ opts.select{ |k, v| @default_options.key?(k) }.each do |k, v|
31
+ @dirty_options[k.to_sym] = v
32
+ end
33
+ save
34
+ end
35
+
36
+ def reset_defaults!
37
+ @dirty_options = {}
38
+ @default_options.each do |key, value|
39
+ configuration_class.where(name: namespaced_key(key)).each(&:destroy)
40
+ end
41
+ end
42
+
43
+ def is_default?(key, value)
44
+ value.to_s == @default_options[key.to_sym].to_s
45
+ end
46
+
47
+ private
48
+
49
+ # ---------------------------------------------------- Method missing magic
50
+ def method_missing(name, *args, &blk)
51
+ if name.to_s =~ /^default_(.*)=$/
52
+ @default_options[$1.to_sym] = args.first
53
+ elsif name.to_s =~ /=$/
54
+ @dirty_options[$`.to_sym] = args.first
55
+ elsif @default_options.key?(name)
56
+ dirty_or_db_setting_or_default(name)
57
+ else
58
+ super
59
+ end
60
+ end
61
+ # --------------------------------------------------- /Method missing magic
62
+
63
+ # This allows us to use the same code in Community and Pro and overwrite
64
+ # the name of the class in an initializer.
65
+ def configuration_class
66
+ @klass ||= Dradis::Plugins::configuration_class.to_s.constantize
67
+ end
68
+
69
+ def write_to_db(key, value)
70
+ # FIXME: how ugly is this?
71
+ # UPGRADE: Rails 4 find_or_create_by_
72
+ db_setting = if Rails::VERSION::MAJOR == 3
73
+ configuration_class.find_or_create_by_name(namespaced_key(key))
74
+ else
75
+ configuration_class.find_or_create_by(name: namespaced_key(key))
76
+ end
77
+ db_setting.update_attribute(:value, value)
78
+ end
79
+
80
+
81
+ def db_setting(key)
82
+ configuration_class.where(name: namespaced_key(key)).first.value rescue nil
83
+ end
84
+
85
+ # This method looks up in the configuration repository DB to see if the
86
+ # user has provided a value for the given setting. If not, the default
87
+ # value is returned.
88
+ def dirty_or_db_setting_or_default(key)
89
+ if @dirty_options.key?(key)
90
+ @dirty_options[key]
91
+ elsif configuration_class.exists?(name: namespaced_key(key))
92
+ db_setting(key)
93
+ else
94
+ @default_options[key]
95
+ end
96
+ end
97
+
98
+ # Builds namespaced key
99
+ def namespaced_key(key)
100
+ [self.namespace.to_s, key.to_s.underscore].join(":")
101
+ end
102
+ end
103
+ end
@@ -4,7 +4,7 @@ module Dradis
4
4
  attr_accessor :logger, :template, :templates_dir
5
5
 
6
6
  def initialize(args={})
7
- @plugin = args[:plugin]
7
+ @plugin = args.fetch(:plugin)
8
8
  @templates_dir = args[:templates_dir] || default_templates_dir
9
9
  end
10
10
 
@@ -64,7 +64,7 @@ module Dradis
64
64
  end
65
65
 
66
66
  # This method returns the current template's source. It caches the
67
- # template based on the file's last-modified time and refereshes the
67
+ # template based on the file's last-modified time and refreshes the
68
68
  # cached copy when it detects changes.
69
69
  def template_source
70
70
  @sources ||= {}
@@ -95,7 +95,10 @@ module Dradis
95
95
  # This method returns the default location in which plugins should look
96
96
  # for their templates.
97
97
  def default_templates_dir
98
- @default_templates_dir ||= Rails.root.join('templates', 'plugins', @plugin::Engine::plugin_name.to_s)
98
+ @default_templates_dir ||= begin
99
+ conf_class = Dradis::Plugins::configuration_class.constantize
100
+ File.join(conf_class.paths_templates_plugins, @plugin::meta[:name].to_s)
101
+ end
99
102
  end
100
103
  end
101
104
  end
@@ -1,27 +1,29 @@
1
1
  module Dradis
2
2
  module Plugins
3
3
  module Templates
4
- def self.included(base)
5
- base.extend ClassMethods
6
-
7
- base.class_eval do
8
- # Keep track of any templates the plugin defines
9
- paths['dradis/templates'] = 'templates'
10
- end
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ # Keep track of any templates the plugin defines
8
+ paths['dradis/templates'] = 'templates'
11
9
  end
12
10
 
13
11
  module ClassMethods
14
12
  def copy_templates(args={})
15
- destination = args[:to]
13
+ destination = args.fetch(:to)
16
14
 
17
- raise ArgumentError.new(':copy_templates called without a :to parameter') unless destination
18
- FileUtils.mkdir_p(destination) if plugin_templates.any?
15
+ destination_dir = File.join(destination, plugin_name.to_s)
16
+ FileUtils.mkdir_p(destination_dir) if plugin_templates.any?
19
17
 
20
18
  plugin_templates.each do |template|
21
- destination_file = File.join(destination, File.basename(template))
22
- next if File.exist?(destination_file)
19
+ destination_file = File.join(destination_dir, File.basename(template))
23
20
 
24
- Rails.logger.info{ "Updating templates for #{plugin_name} plugin. Destination: #{destination}" }
21
+ next if skip?(destination_file)
22
+
23
+ Rails.logger.info do
24
+ "Updating templates for #{plugin_name} plugin. "\
25
+ "Destination: #{destination}"
26
+ end
25
27
  FileUtils.cp(template, destination_file)
26
28
  end
27
29
  end
@@ -29,13 +31,27 @@ module Dradis
29
31
  def plugin_templates(args={})
30
32
  @templates ||= begin
31
33
  if paths['dradis/templates'].existent.any?
32
- Dir["%s/*" % paths['dradis/templates'].existent]
34
+ Dir["#{paths['dradis/templates'].existent.first}/*"]
33
35
  else
34
36
  []
35
37
  end
36
38
  end
37
39
  end
40
+
41
+ private
42
+
43
+ # Normally we want to copy all templates so that the user always has
44
+ # the latest version.
45
+ #
46
+ # However, if it's a '.template' file, the user might have edited their
47
+ # local copy, and we don't want to override their changes. So only
48
+ # copy .template files over if the user has no copy at all (i.e. if
49
+ # this is the first time they've started Dradis since this template was
50
+ # added.)
51
+ def skip?(file_path)
52
+ File.exist?(file_path) && File.extname(file_path) == ".template"
53
+ end
38
54
  end
39
55
  end
40
56
  end
41
- end
57
+ end
@@ -0,0 +1,30 @@
1
+ module Dradis
2
+ module Plugins
3
+ module Thor
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+
7
+ base.class_eval do
8
+ # Keep track of any templates the plugin defines
9
+ paths['dradis/thorfiles'] = 'lib/tasks'
10
+ end
11
+ end
12
+
13
+ module ClassMethods
14
+ def load_thor_tasks
15
+ plugin_thorfiles.each do |thorfile|
16
+ require thorfile
17
+ end
18
+ end
19
+
20
+ def plugin_thorfiles(args={})
21
+ if paths['dradis/thorfiles'].existent.any?
22
+ Dir["%s/thorfile.rb" % paths['dradis/thorfiles'].existent]
23
+ else
24
+ []
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,14 @@
1
+ module Dradis
2
+ module Plugins
3
+ # Helper methods for plugin Thor tasks
4
+ module ThorHelper
5
+ def content_service_for(plugin)
6
+ Dradis::Plugins::ContentService.new(plugin: plugin)
7
+ end
8
+
9
+ def detect_and_set_project_scope
10
+ ;
11
+ end
12
+ end
13
+ end
14
+ end
@@ -1,2 +1,10 @@
1
+ module Dradis
2
+ module Plugins
3
+ module Upload
4
+ end
5
+ end
6
+ end
7
+
1
8
  require 'dradis/plugins/upload/base'
9
+ require 'dradis/plugins/upload/importer'
2
10
  require 'dradis/plugins/upload/field_processor'
@@ -1,42 +1,57 @@
1
- # This module contains basic Upload plugin functions to control template
2
- # sample and field management for the Plugin Manager.
3
- #
4
- module Dradis
5
- module Plugins
6
- module Upload
7
- class Base
8
- attr_accessor :content_service, :logger, :template_service
9
-
10
- def initialize(args={})
11
- @logger = args.fetch(:logger, Rails.logger)
12
-
13
- @content_service = args[:content_service] || default_content_service
14
- @template_service = args[:template_service] || default_template_service
15
-
16
- content_service.logger = logger
17
- template_service.logger = logger
18
-
19
- post_initialize(args)
20
- end
21
-
22
- def import(args={})
23
- raise "The import() method is not implemented in this plugin [#{self.class.name}]."
24
- end
25
-
26
- # This method can be overwriten by plugins to do initialization tasks.
27
- def post_initialize(args={})
28
- end
29
1
 
30
- private
31
- def default_content_service
32
- @content ||= Dradis::Plugins::ContentService.new
33
- end
34
-
35
- def default_template_service
36
- @template ||= Dradis::Plugins::TemplateService.new
37
- end
38
- end # Base
2
+ # When you call provides :upload in your Engine, this module gets included. It
3
+ # provides two features:
4
+ #
5
+ # a) an .uploaders() class method that by default responds with a single array
6
+ # item pointing to the engine's parent. This will be used by the framework to
7
+ # locate the ::Importer object that does the upload heavy lifting.
8
+ #
9
+ # If your plugin implements more than one uploader, each one would be contained
10
+ # in its own namespace, and you should overwrite the .uploaders() method to
11
+ # return an array of all these namespaces. See method definition for an
12
+ # example.
13
+ #
14
+ # b) it adds a .meta() method to the engine's parent module, containing the
15
+ # name, description and version of the add-on.
16
+ #
17
+ # Again, if you implement more than one uploader, make sure you create a
18
+ # .meta() class-level method in each of your namespaces.
19
+ #
39
20
 
40
- end # Upload
41
- end # Plugins
42
- end # Core
21
+ module Dradis::Plugins::Upload::Base
22
+ extend ActiveSupport::Concern
23
+
24
+ included do
25
+ parent.extend NamespaceClassMethods
26
+ end
27
+
28
+ module ClassMethods
29
+ # Return the list of modules that provide upload functionality. This is
30
+ # useful if one plugin provides uploading functionality for more than one
31
+ # file type (e.g. the Projects plugin allows you to upload a Package or a
32
+ # Template).
33
+ #
34
+ # The default implementation just returns this plugin's namespace (e.g.
35
+ # Dradis::Plugins::Nessus). If a plugin provides multiple uploaders, they
36
+ # can override this method:
37
+ # def self.uploders
38
+ # [
39
+ # Dradis::Plugins::Projects::Package,
40
+ # Dradis::Plugins::Projects::Template
41
+ # ]
42
+ # end
43
+ def uploaders()
44
+ [parent]
45
+ end
46
+ end
47
+
48
+ module NamespaceClassMethods
49
+ def meta
50
+ {
51
+ name: self::Engine::plugin_name,
52
+ description: self::Engine::plugin_description,
53
+ version: self::VERSION::STRING
54
+ }
55
+ end
56
+ end
57
+ end
@@ -22,6 +22,12 @@ module Dradis
22
22
  field = args[:field]
23
23
  "Sorry, this plugin doesn't define a FieldProcessor (called for [#{field}])"
24
24
  end
25
+
26
+ protected
27
+ # This can be overriden by subclasses
28
+ def post_initialize(args={})
29
+ # nop
30
+ end
25
31
  end
26
32
 
27
33
  end
@@ -0,0 +1,42 @@
1
+ # This module contains basic Upload plugin functions to control template
2
+ # sample and field management for the Plugin Manager.
3
+ #
4
+ module Dradis
5
+ module Plugins
6
+ module Upload
7
+ class Importer
8
+ attr_accessor :content_service, :logger, :template_service
9
+
10
+ def initialize(args={})
11
+ @logger = args.fetch(:logger, Rails.logger)
12
+
13
+ @content_service = args[:content_service] || default_content_service
14
+ @template_service = args[:template_service] || default_template_service
15
+
16
+ content_service.logger = logger
17
+ template_service.logger = logger
18
+
19
+ post_initialize(args)
20
+ end
21
+
22
+ def import(args={})
23
+ raise "The import() method is not implemented in this plugin [#{self.class.name}]."
24
+ end
25
+
26
+ # This method can be overwriten by plugins to do initialization tasks.
27
+ def post_initialize(args={})
28
+ end
29
+
30
+ private
31
+ def default_content_service
32
+ @content ||= Dradis::Plugins::ContentService.new
33
+ end
34
+
35
+ def default_template_service
36
+ @template ||= Dradis::Plugins::TemplateService.new
37
+ end
38
+ end # Importer
39
+
40
+ end # Upload
41
+ end # Plugins
42
+ end # Core
File without changes
@@ -0,0 +1,88 @@
1
+ #
2
+ # This spec must be ran from Dradis root dir
3
+ #
4
+ require 'spec_helper'
5
+
6
+ class TestEngine < ::Rails::Engine
7
+ include ::Dradis::Plugins::Base
8
+ addon_settings :test_engine do
9
+ settings.default_host = 'localhost'
10
+ settings.default_port = 80
11
+ settings.default_protocol = 'http'
12
+ end
13
+ end
14
+
15
+ describe Dradis::Plugins::Settings do
16
+
17
+ before(:each) do
18
+ TestEngine::settings.reset_defaults!
19
+ end
20
+
21
+ it "sets and return default values" do
22
+ expect(TestEngine::settings.host).to eq('localhost')
23
+ expect(TestEngine::settings.port).to eq(80)
24
+ end
25
+
26
+ it "sets and returns user defined values" do
27
+ expect(TestEngine::settings.host).to eq('localhost')
28
+ TestEngine::settings.host = '127.0.0.1'
29
+ expect(TestEngine::settings.host).to eq('127.0.0.1')
30
+ expect(TestEngine::settings.port).to eq(80)
31
+ end
32
+
33
+ it "sets and returns new value even if it equals default value" do
34
+ expect(TestEngine::settings.host).to eq('localhost')
35
+ TestEngine::settings.host = '127.0.0.1'
36
+ expect(TestEngine::settings.host).to eq('127.0.0.1')
37
+ TestEngine::settings.host = 'localhost'
38
+ expect(TestEngine::settings.host).to eq('localhost')
39
+ end
40
+
41
+ it "saves to db and returns persisted values" do
42
+ expect(TestEngine::settings.host).to eq('localhost')
43
+ TestEngine::settings.host = '127.0.0.1'
44
+ expect_any_instance_of(TestEngine::settings::send(:configuration_class)).to receive(:update_attribute)
45
+ expect(TestEngine::settings.save).to eq( { host: '127.0.0.1'} )
46
+ expect(TestEngine::settings.host).to eq('127.0.0.1')
47
+ end
48
+
49
+ it "reads from db after saving" do
50
+ expect(TestEngine::settings.host).to eq('localhost')
51
+ TestEngine::settings.host = '127.0.0.1'
52
+ expect(TestEngine::settings.save).to eq( { host: '127.0.0.1'} )
53
+ end
54
+
55
+ end
56
+
57
+ describe Dradis::Plugins::Settings, '#is_default?' do
58
+ it 'knows if a string value equals its default integer value' do
59
+ TestEngine::settings.is_default?(:port, '80')
60
+ end
61
+ end
62
+
63
+ describe Dradis::Plugins::Settings, '#all' do
64
+ it 'returns values from db, dirty state or default as needed and tells which one is default' do
65
+ TestEngine::settings.host = '127.0.0.1'
66
+ TestEngine::settings.save
67
+ TestEngine::settings.protocol = 'https'
68
+ expect(TestEngine::settings.all).to eq([
69
+ {
70
+ name: :host,
71
+ value: '127.0.0.1',
72
+ default: false
73
+ },
74
+ {
75
+ name: :port,
76
+ value: 80,
77
+ default: true
78
+ },
79
+ {
80
+ name: :protocol,
81
+ value: 'https',
82
+ default: false
83
+ },
84
+ ])
85
+ end
86
+ end
87
+
88
+
@@ -0,0 +1,2 @@
1
+ RSpec.configure do |config|
2
+ end
metadata CHANGED
@@ -1,43 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dradis-plugins
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 3.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Martin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-18 00:00:00.000000000 Z
11
+ date: 2016-06-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.6'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.6'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ~>
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
33
  version: '10.0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ~>
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec-rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  description: Required dependency for Dradis Framework.
42
56
  email:
43
57
  - etd@nomejortu.com
@@ -45,15 +59,19 @@ executables: []
45
59
  extensions: []
46
60
  extra_rdoc_files: []
47
61
  files:
48
- - .gitignore
62
+ - ".gitignore"
63
+ - ".rspec"
49
64
  - CONTRIBUTING.md
50
65
  - Gemfile
51
66
  - LICENSE
52
67
  - README.md
53
68
  - Rakefile
69
+ - app/controllers/dradis/plugins/export/base_controller.rb
54
70
  - dradis-plugins.gemspec
55
71
  - lib/dradis-plugins.rb
56
72
  - lib/dradis/plugins.rb
73
+ - lib/dradis/plugins/base.rb
74
+ - lib/dradis/plugins/configurable.rb
57
75
  - lib/dradis/plugins/content_service.rb
58
76
  - lib/dradis/plugins/engine.rb
59
77
  - lib/dradis/plugins/export.rb
@@ -63,12 +81,19 @@ files:
63
81
  - lib/dradis/plugins/import/filters.rb
64
82
  - lib/dradis/plugins/import/filters/base.rb
65
83
  - lib/dradis/plugins/import/result.rb
84
+ - lib/dradis/plugins/settings.rb
66
85
  - lib/dradis/plugins/template_service.rb
67
86
  - lib/dradis/plugins/templates.rb
87
+ - lib/dradis/plugins/thor.rb
88
+ - lib/dradis/plugins/thor_helper.rb
68
89
  - lib/dradis/plugins/upload.rb
69
90
  - lib/dradis/plugins/upload/base.rb
70
91
  - lib/dradis/plugins/upload/field_processor.rb
92
+ - lib/dradis/plugins/upload/importer.rb
71
93
  - lib/dradis/plugins/version.rb
94
+ - spec/internal/log/test.log
95
+ - spec/settings_spec.rb
96
+ - spec/spec_helper.rb
72
97
  homepage: http://dradisframework.org
73
98
  licenses:
74
99
  - GPL-2
@@ -79,18 +104,21 @@ require_paths:
79
104
  - lib
80
105
  required_ruby_version: !ruby/object:Gem::Requirement
81
106
  requirements:
82
- - - '>='
107
+ - - ">="
83
108
  - !ruby/object:Gem::Version
84
109
  version: '0'
85
110
  required_rubygems_version: !ruby/object:Gem::Requirement
86
111
  requirements:
87
- - - '>='
112
+ - - ">="
88
113
  - !ruby/object:Gem::Version
89
114
  version: '0'
90
115
  requirements: []
91
116
  rubyforge_project:
92
- rubygems_version: 2.3.0
117
+ rubygems_version: 2.2.3
93
118
  signing_key:
94
119
  specification_version: 4
95
120
  summary: Plugin manager for the Dradis Framework project.
96
- test_files: []
121
+ test_files:
122
+ - spec/internal/log/test.log
123
+ - spec/settings_spec.rb
124
+ - spec/spec_helper.rb