dradis-projects 4.1.1 → 4.3.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
  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