dradis-plugins 3.0.0 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
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