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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b8f7035cd71c220abd7de8ba0ac994663cd2181a98e29c9f320de14e9a4122c4
4
- data.tar.gz: 15ae46a145265e5c54880c0e47747efb0246b25bf7c36c1bc2717523f77729d2
3
+ metadata.gz: 2c39ba4b5ddb30f2479c7338e57ae63bebeaf1d6e8c6ff43ab7b2aa2bbff8adf
4
+ data.tar.gz: 641e756e4f05fb16909d6c27af88cc59cf098ca5772abe936f528268850cde56
5
5
  SHA512:
6
- metadata.gz: 94cf726365809dfe78d7ad235a1ce5a0b18a0e8c4d4b35b6d4707cab777f758918b659671eed99c86cb3d26208966febcbc06ca7f57a95108e8c19caa5e73f7d
7
- data.tar.gz: 5ba40bacbcd2335f580f11faf25cb8a6812b3658061d7edaf5ed812e66f99960d9fef4374a20fb6fd7e7961cb39b350c2ded3c34e04669c4b51867b6016fe9cc
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
 
@@ -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', '~> 1.6'
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'
@@ -8,8 +8,8 @@ module Dradis
8
8
 
9
9
  module VERSION
10
10
  MAJOR = 4
11
- MINOR = 1
12
- TINY = 1
11
+ MINOR = 3
12
+ TINY = 0
13
13
  PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
@@ -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
- if File.directory? Rails.root.join('tmp', 'zip', oldid)
52
- FileUtils.mkdir_p Attachment.pwd.join(newid.to_s)
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
- Dir.glob(Rails.root.join('tmp', 'zip', oldid, '*')).each do |attachment|
55
- FileUtils.mv(attachment, Attachment.pwd.join(newid.to_s))
56
- end
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).gsub(ATTACHMENT_URL) do |_|
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.to_s]
119
+ evidence.issue_id = lookup_table[:issues][evidence.issue_id]
122
120
 
123
- new_content = evidence.content.gsub(ATTACHMENT_URL) do |_|
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.to_s]
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
- lookup_table[:nodes][xml_node.at_xpath('id').text.strip] = node.id
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
- lookup_table[:cards][xml_card.at_xpath('id').text.to_i] = card.id
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][xml_list.at_xpath('id').text.to_i] = list.id
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.1.1
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: 2021-11-18 00:00:00.000000000 Z
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: '1.6'
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: '1.6'
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.2.28
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