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 +4 -4
- data/.rspec +2 -0
- data/README.md +3 -3
- data/app/controllers/dradis/plugins/export/base_controller.rb +9 -0
- data/dradis-plugins.gemspec +1 -0
- data/lib/dradis/plugins.rb +25 -48
- data/lib/dradis/plugins/base.rb +45 -0
- data/lib/dradis/plugins/configurable.rb +26 -0
- data/lib/dradis/plugins/content_service.rb +120 -20
- data/lib/dradis/plugins/engine.rb +7 -1
- data/lib/dradis/plugins/export/base.rb +12 -3
- data/lib/dradis/plugins/gem_version.rb +4 -4
- data/lib/dradis/plugins/import/filters.rb +7 -6
- data/lib/dradis/plugins/import/result.rb +4 -3
- data/lib/dradis/plugins/settings.rb +103 -0
- data/lib/dradis/plugins/template_service.rb +6 -3
- data/lib/dradis/plugins/templates.rb +31 -15
- data/lib/dradis/plugins/thor.rb +30 -0
- data/lib/dradis/plugins/thor_helper.rb +14 -0
- data/lib/dradis/plugins/upload.rb +8 -0
- data/lib/dradis/plugins/upload/base.rb +55 -40
- data/lib/dradis/plugins/upload/field_processor.rb +6 -0
- data/lib/dradis/plugins/upload/importer.rb +42 -0
- data/spec/internal/log/test.log +0 -0
- data/spec/settings_spec.rb +88 -0
- data/spec/spec_helper.rb +2 -0
- metadata +39 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 74496768f4fd445889de85903ef1470ea133c87f
|
4
|
+
data.tar.gz: 71a03d2ef7b24237a75f87b3ee9295afb963b0ec
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8f1d927b2727a5e010fcd889a37fc858520b309ab89decf49d4c32d222fe60a0654a05b9a5ad5a7f9d2d14616cf0383c8827325f100272bc8b30834354f42b35
|
7
|
+
data.tar.gz: 48f722e8962d1f4a07e26b5d975fe2b98097c7b666c0255e6424ceafc67f8f635c7925719b55d41c792265347cfe7afd190eb960bff84da3c69d5fd6d88bcba8
|
data/.rspec
ADDED
data/README.md
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# Plugin manager for the Dradis Framework
|
2
2
|
|
3
|
-
[](http://travis-ci.org/dradis/dradis-plugins)
|
3
|
+
[](http://travis-ci.org/dradis/dradis-plugins) [](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
|
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.
|
data/dradis-plugins.gemspec
CHANGED
@@ -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
|
data/lib/dradis/plugins.rb
CHANGED
@@ -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
|
-
|
16
|
-
|
17
|
-
|
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
|
-
|
40
|
-
|
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
|
-
|
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
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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
|
28
|
-
parent
|
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
|
-
|
31
|
-
|
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.
|
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#{
|
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).
|
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.
|
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
|
-
"
|
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).
|
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
|
@@ -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}
|
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[
|
32
|
+
_filters[plugin] = {} unless _filters.key?(plugin)
|
33
|
+
_filters[plugin][label] = filter
|
33
34
|
end
|
34
35
|
|
35
|
-
# Provides access to
|
36
|
+
# Provides access to filters by plugin
|
36
37
|
# :api: public
|
37
|
-
def [](
|
38
|
-
_filters[
|
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
|
-
@
|
8
|
-
@
|
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
|
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
|
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 ||=
|
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
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
13
|
+
destination = args.fetch(:to)
|
16
14
|
|
17
|
-
|
18
|
-
FileUtils.mkdir_p(
|
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(
|
22
|
-
next if File.exist?(destination_file)
|
19
|
+
destination_file = File.join(destination_dir, File.basename(template))
|
23
20
|
|
24
|
-
|
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["
|
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,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
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
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
|
-
|
41
|
-
|
42
|
-
|
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
|
@@ -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
|
+
|
data/spec/spec_helper.rb
ADDED
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.
|
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:
|
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
|
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
|