dradis-projects 4.1.1 → 4.3.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/CHANGELOG.md +12 -0
- data/dradis-projects.gemspec +1 -1
- data/lib/dradis/plugins/projects/gem_version.rb +2 -2
- data/lib/dradis/plugins/projects/upload/package.rb +11 -6
- data/lib/dradis/plugins/projects/upload/v1/template.rb +26 -14
- data/lib/dradis/plugins/projects/upload/v3/template.rb +7 -5
- data/spec/fixtures/files/malformed_ids.xml +14 -0
- data/spec/fixtures/files/missing_node.xml +11 -0
- data/spec/lib/dradis/plugins/projects/upload/v1/template_spec.rb +47 -4
- metadata +9 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2c39ba4b5ddb30f2479c7338e57ae63bebeaf1d6e8c6ff43ab7b2aa2bbff8adf
|
4
|
+
data.tar.gz: 641e756e4f05fb16909d6c27af88cc59cf098ca5772abe936f528268850cde56
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d72658eeb7da4cec64c115606049dbab50226ea2bd8ceef3f35a1d9e8d3381b828053b0be993d8207b96952e026d5b28f684d683e14f8f720d8e08d58b28016d
|
7
|
+
data.tar.gz: 9c5435993d5ca16a61edfa0093897a7702b9618ebe298e8cf3928046b1a7b750d416dc536454a496d133e674a065167d170496591a6dc2482f69bc8ca3257326
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
v4.3.0 (April 2022)
|
2
|
+
- No changes
|
3
|
+
|
4
|
+
v4.2.0 (February 2022)
|
5
|
+
- Bugs fixes:
|
6
|
+
- Fix missing nodes for attachments during template and package imports
|
7
|
+
- Fix missing parent nodes during template and package imports
|
8
|
+
|
9
|
+
v4.1.2.1 (December 2021)
|
10
|
+
- Security Fixes:
|
11
|
+
- High: Authenticated author path traversal
|
12
|
+
|
1
13
|
v4.1.1 (November 2021)
|
2
14
|
- Loosen dradis-plugins version requirement
|
3
15
|
|
data/dradis-projects.gemspec
CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.executables = spec.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
21
21
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
22
22
|
|
23
|
-
spec.add_development_dependency 'bundler', '~>
|
23
|
+
spec.add_development_dependency 'bundler', '~> 2.2'
|
24
24
|
spec.add_development_dependency 'combustion'
|
25
25
|
spec.add_development_dependency 'rake', '~> 10.0'
|
26
26
|
spec.add_development_dependency 'rspec'
|
@@ -47,13 +47,18 @@ module Dradis::Plugins::Projects::Upload
|
|
47
47
|
|
48
48
|
|
49
49
|
logger.info { 'Moving attachments to their final destinations...' }
|
50
|
-
lookup_table[:nodes].each do |oldid,newid|
|
51
|
-
|
52
|
-
|
50
|
+
lookup_table[:nodes].each do |oldid, newid|
|
51
|
+
tmp_dir = Rails.root.join('tmp', 'zip')
|
52
|
+
old_attachments_dir = File.expand_path(tmp_dir.join(oldid.to_s))
|
53
53
|
|
54
|
-
|
55
|
-
|
56
|
-
|
54
|
+
# Ensure once the path is expanded it's still within the expected
|
55
|
+
# tmp directory to prevent unauthorized access to other dirs
|
56
|
+
next unless old_attachments_dir.starts_with?(tmp_dir.to_s) && File.directory?(old_attachments_dir)
|
57
|
+
|
58
|
+
FileUtils.mkdir_p Attachment.pwd.join(newid.to_s)
|
59
|
+
|
60
|
+
Dir.glob(Pathname.new(old_attachments_dir).join('*')).each do |attachment|
|
61
|
+
FileUtils.mv(attachment, Attachment.pwd.join(newid.to_s))
|
57
62
|
end
|
58
63
|
end
|
59
64
|
logger.info { 'Done.' }
|
@@ -104,9 +104,7 @@ module Dradis::Plugins::Projects::Upload::V1
|
|
104
104
|
|
105
105
|
logger.info { "Adjusting screenshot URLs: #{item.class.name} ##{item.id}" }
|
106
106
|
|
107
|
-
new_text = item.send(text_attr)
|
108
|
-
"!%s/projects/%d/nodes/%d/attachments/%s!" % [$1, project.id, lookup_table[:nodes][$2], $3]
|
109
|
-
end
|
107
|
+
new_text = update_attachment_references(item.send(text_attr))
|
110
108
|
item.send(text_attr.to_s + "=", new_text)
|
111
109
|
|
112
110
|
raise "Couldn't save note attachment URL for #{item.class.name} ##{item.id}" unless validate_and_save(item)
|
@@ -118,12 +116,9 @@ module Dradis::Plugins::Projects::Upload::V1
|
|
118
116
|
def finalize_evidence
|
119
117
|
pending_changes[:evidence].each_with_index do |evidence, i|
|
120
118
|
logger.info { "Setting issue_id for evidence" }
|
121
|
-
evidence.issue_id = lookup_table[:issues][evidence.issue_id
|
119
|
+
evidence.issue_id = lookup_table[:issues][evidence.issue_id]
|
122
120
|
|
123
|
-
|
124
|
-
"!%s/projects/%d/nodes/%d/attachments/%s!" % [$1, project.id, lookup_table[:nodes][$2], $3]
|
125
|
-
end
|
126
|
-
evidence.content = new_content
|
121
|
+
evidence.content = update_attachment_references(evidence.content)
|
127
122
|
|
128
123
|
raise "Couldn't save Evidence :issue_id / attachment URL Evidence ##{evidence.id}" unless validate_and_save(evidence)
|
129
124
|
|
@@ -141,7 +136,7 @@ module Dradis::Plugins::Projects::Upload::V1
|
|
141
136
|
def finalize_nodes
|
142
137
|
pending_changes[:orphan_nodes].each do |node|
|
143
138
|
logger.info { "Finding parent for orphaned node: #{node.label}. Former parent was #{node.parent_id}" }
|
144
|
-
node.parent_id = lookup_table[:nodes][node.parent_id
|
139
|
+
node.parent_id = lookup_table[:nodes][node.parent_id]
|
145
140
|
raise "Couldn't save node parent for Node ##{node.id}" unless validate_and_save(node)
|
146
141
|
end
|
147
142
|
end
|
@@ -153,7 +148,7 @@ module Dradis::Plugins::Projects::Upload::V1
|
|
153
148
|
logger.info { 'Processing Categories...' }
|
154
149
|
|
155
150
|
template.xpath('dradis-template/categories/category').each do |xml_category|
|
156
|
-
old_id = xml_category.at_xpath('id').text.strip
|
151
|
+
old_id = Integer(xml_category.at_xpath('id').text.strip)
|
157
152
|
name = xml_category.at_xpath('name').text.strip
|
158
153
|
category = nil
|
159
154
|
|
@@ -183,7 +178,7 @@ module Dradis::Plugins::Projects::Upload::V1
|
|
183
178
|
pending_changes[:attachment_notes] << issue
|
184
179
|
end
|
185
180
|
|
186
|
-
old_id = xml_issue.at_xpath('id').text.strip
|
181
|
+
old_id = Integer(xml_issue.at_xpath('id').text.strip)
|
187
182
|
lookup_table[:issues][old_id] = issue.id
|
188
183
|
logger.info{ "New issue detected: #{issue.title}" }
|
189
184
|
end
|
@@ -289,7 +284,12 @@ module Dradis::Plugins::Projects::Upload::V1
|
|
289
284
|
node = parse_node(xml_node)
|
290
285
|
|
291
286
|
# keep track of reassigned ids
|
292
|
-
|
287
|
+
# Convert the id to an integer as it has no place being a string, or
|
288
|
+
# directory path. We later use this ID to build a directory structure
|
289
|
+
# to place attachments and without validation opens the potential for
|
290
|
+
# path traversal.
|
291
|
+
node_original_id = Integer(xml_node.at_xpath('id').text.strip)
|
292
|
+
lookup_table[:nodes][node_original_id] = node.id
|
293
293
|
end
|
294
294
|
|
295
295
|
logger.info { 'Done.' }
|
@@ -326,7 +326,7 @@ module Dradis::Plugins::Projects::Upload::V1
|
|
326
326
|
xml_node.xpath('notes/note').each do |xml_note|
|
327
327
|
|
328
328
|
if xml_note.at_xpath('author') != nil
|
329
|
-
old_id = xml_note.at_xpath('category-id').text.strip
|
329
|
+
old_id = Integer(xml_note.at_xpath('category-id').text.strip)
|
330
330
|
new_id = lookup_table[:categories][old_id]
|
331
331
|
|
332
332
|
created_at = xml_note.at_xpath('created-at')
|
@@ -370,7 +370,7 @@ module Dradis::Plugins::Projects::Upload::V1
|
|
370
370
|
logger.info { "New tag detected: #{name}" }
|
371
371
|
|
372
372
|
xml_tag.xpath('./taggings/tagging').each do |xml_tagging|
|
373
|
-
old_taggable_id = xml_tagging.at_xpath('taggable-id').text()
|
373
|
+
old_taggable_id = Integer(xml_tagging.at_xpath('taggable-id').text())
|
374
374
|
taggable_type = xml_tagging.at_xpath('taggable-type').text()
|
375
375
|
|
376
376
|
new_taggable_id = case taggable_type
|
@@ -394,6 +394,18 @@ module Dradis::Plugins::Projects::Upload::V1
|
|
394
394
|
end
|
395
395
|
end
|
396
396
|
|
397
|
+
def update_attachment_references(string)
|
398
|
+
string.gsub(ATTACHMENT_URL) do |attachment|
|
399
|
+
node_id = lookup_table[:nodes][$2.to_i]
|
400
|
+
if node_id
|
401
|
+
"!%s/projects/%d/nodes/%d/attachments/%s!" % [$1, project.id, node_id, $3]
|
402
|
+
else
|
403
|
+
logger.error { "The attachment wasn't included in the package: #{attachment}" }
|
404
|
+
attachment
|
405
|
+
end
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
397
409
|
def user_id_for_email(email)
|
398
410
|
users[email] || @default_user_id
|
399
411
|
end
|
@@ -94,7 +94,7 @@ module Dradis::Plugins::Projects::Upload::V3
|
|
94
94
|
card = list.cards.create name: xml_card.at_xpath('name').text,
|
95
95
|
description: xml_card.at_xpath('description').text,
|
96
96
|
due_date: due_date,
|
97
|
-
previous_id: xml_card.at_xpath('previous_id').text
|
97
|
+
previous_id: xml_card.at_xpath('previous_id').text&.to_i
|
98
98
|
|
99
99
|
xml_card.xpath('activities/activity').each do |xml_activity|
|
100
100
|
raise "Couldn't create activity for Card ##{card.id}" unless create_activity(card, xml_activity)
|
@@ -106,7 +106,8 @@ module Dradis::Plugins::Projects::Upload::V3
|
|
106
106
|
|
107
107
|
raise "Couldn't create comments for Card ##{card.id}" unless create_comments(card, xml_card.xpath('comments/comment'))
|
108
108
|
|
109
|
-
|
109
|
+
xml_id = Integer(xml_card.at_xpath('id').text)
|
110
|
+
lookup_table[:cards][xml_id] = card.id
|
110
111
|
pending_changes[:cards] << card
|
111
112
|
end
|
112
113
|
|
@@ -131,7 +132,7 @@ module Dradis::Plugins::Projects::Upload::V3
|
|
131
132
|
xml_node_id = xml_board.at_xpath('node_id').try(:text)
|
132
133
|
node_id =
|
133
134
|
if xml_node_id.present?
|
134
|
-
lookup_table[:nodes][xml_node_id]
|
135
|
+
lookup_table[:nodes][xml_node_id.to_i]
|
135
136
|
else
|
136
137
|
project.methodology_library.id
|
137
138
|
end
|
@@ -143,9 +144,10 @@ module Dradis::Plugins::Projects::Upload::V3
|
|
143
144
|
|
144
145
|
xml_board.xpath('./list').each do |xml_list|
|
145
146
|
list = board.lists.create name: xml_list.at_xpath('name').text,
|
146
|
-
previous_id: xml_list.at_xpath('previous_id').text
|
147
|
+
previous_id: xml_list.at_xpath('previous_id').text&.to_i
|
148
|
+
xml_id = Integer(xml_list.at_xpath('id').text)
|
147
149
|
|
148
|
-
lookup_table[:lists][
|
150
|
+
lookup_table[:lists][xml_id] = list.id
|
149
151
|
pending_changes[:lists] << list
|
150
152
|
|
151
153
|
xml_list.xpath('./card').each do |xml_card|
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<dradis-template version="1">
|
3
|
+
<nodes>
|
4
|
+
<node>
|
5
|
+
<id>../../../../../../tmp</id>
|
6
|
+
<label>Node 1</label>
|
7
|
+
<parent-id/>
|
8
|
+
<position>0</position>
|
9
|
+
<properties><![CDATA[{
|
10
|
+
}]]></properties>
|
11
|
+
<type-id>1</type-id>
|
12
|
+
</node>
|
13
|
+
</nodes>
|
14
|
+
</dradis-template>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<dradis-template version="2">
|
3
|
+
<nodes><node><id>5</id><label>Uploaded files</label><parent-id/><position>0</position><properties><![CDATA[{}]]></properties><type-id>0</type-id><notes></notes><evidence></evidence><activities></activities></node></nodes>
|
4
|
+
<issues><issue><id>2</id><author>admin@securityroots.com</author><text><![CDATA[#[Title]#
|
5
|
+
Test Issue
|
6
|
+
|
7
|
+
#[Description]#
|
8
|
+
!/pro/projects/222/nodes/12345/attachments/hello.jpg!
|
9
|
+
|
10
|
+
]]></text><activities></activities><comments></comments></issue></issues>
|
11
|
+
</dradis-template>
|
@@ -1,15 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rails_helper'
|
2
4
|
|
3
5
|
describe Dradis::Plugins::Projects::Upload::V1::Template::Importer do
|
4
|
-
|
5
6
|
let(:project) { create(:project) }
|
6
7
|
let(:user) { create(:user) }
|
7
8
|
let(:importer_class) { Dradis::Plugins::Projects::Upload::Template }
|
8
|
-
let(:file_path) {
|
9
|
-
File.join(File.dirname(__FILE__), '../../../../../../', 'fixtures', 'files', 'attachments_url.xml')
|
10
|
-
}
|
11
9
|
|
12
10
|
context 'uploading a template with attachments url' do
|
11
|
+
let(:file_path) do
|
12
|
+
File.join(File.dirname(__FILE__), '../../../../../../', 'fixtures', 'files', 'attachments_url.xml')
|
13
|
+
end
|
14
|
+
|
13
15
|
it 'converts the urls' do
|
14
16
|
importer = importer_class::Importer.new(
|
15
17
|
default_user_id: user.id,
|
@@ -30,4 +32,45 @@ describe Dradis::Plugins::Projects::Upload::V1::Template::Importer do
|
|
30
32
|
)
|
31
33
|
end
|
32
34
|
end
|
35
|
+
|
36
|
+
context 'uploading a template malformed paths as ids' do
|
37
|
+
let(:file_path) do
|
38
|
+
File.join(File.dirname(__FILE__), '../../../../../../', 'fixtures', 'files', 'malformed_ids.xml')
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'returns false' do
|
42
|
+
importer = importer_class::Importer.new(
|
43
|
+
default_user_id: user.id,
|
44
|
+
plugin: importer_class,
|
45
|
+
project_id: project.id
|
46
|
+
)
|
47
|
+
|
48
|
+
expect(importer.import(file: file_path)).to be false
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'uploading a template with attachment but missing node' do
|
53
|
+
let(:file_path) do
|
54
|
+
File.join(File.dirname(__FILE__), '../../../../../../', 'fixtures', 'files', 'missing_node.xml')
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'does not modify the attachment' do
|
58
|
+
logger = double('logger')
|
59
|
+
allow(logger).to receive_messages(debug: nil, error: nil, fatal: nil, info: nil)
|
60
|
+
expect(logger).to receive(:error).once
|
61
|
+
|
62
|
+
importer = importer_class::Importer.new(
|
63
|
+
default_user_id: user.id,
|
64
|
+
logger: logger,
|
65
|
+
plugin: importer_class,
|
66
|
+
project_id: project.id
|
67
|
+
)
|
68
|
+
|
69
|
+
importer.import(file: file_path)
|
70
|
+
|
71
|
+
expect(project.issues.first.text).to include(
|
72
|
+
"!/pro/projects/222/nodes/12345/attachments/hello.jpg!"
|
73
|
+
)
|
74
|
+
end
|
75
|
+
end
|
33
76
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dradis-projects
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.3.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: 2022-04-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '2.2'
|
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
|
-
version: '
|
26
|
+
version: '2.2'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: combustion
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -134,6 +134,8 @@ files:
|
|
134
134
|
- lib/dradis/plugins/projects/version.rb
|
135
135
|
- lib/tasks/thorfile.rb
|
136
136
|
- spec/fixtures/files/attachments_url.xml
|
137
|
+
- spec/fixtures/files/malformed_ids.xml
|
138
|
+
- spec/fixtures/files/missing_node.xml
|
137
139
|
- spec/fixtures/files/with_comments.xml
|
138
140
|
- spec/lib/dradis/plugins/projects/export/v2/template_spec.rb
|
139
141
|
- spec/lib/dradis/plugins/projects/upload/v1/template_spec.rb
|
@@ -157,12 +159,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
157
159
|
- !ruby/object:Gem::Version
|
158
160
|
version: '0'
|
159
161
|
requirements: []
|
160
|
-
rubygems_version: 3.
|
162
|
+
rubygems_version: 3.1.4
|
161
163
|
signing_key:
|
162
164
|
specification_version: 4
|
163
165
|
summary: Project export/upload for the Dradis Framework.
|
164
166
|
test_files:
|
165
167
|
- spec/fixtures/files/attachments_url.xml
|
168
|
+
- spec/fixtures/files/malformed_ids.xml
|
169
|
+
- spec/fixtures/files/missing_node.xml
|
166
170
|
- spec/fixtures/files/with_comments.xml
|
167
171
|
- spec/lib/dradis/plugins/projects/export/v2/template_spec.rb
|
168
172
|
- spec/lib/dradis/plugins/projects/upload/v1/template_spec.rb
|