dradis-projects 5.2.0 → 5.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: 1cd47c15c34228348be8518525b3266837fa5df9fecbf8149b6b84900983e279
4
- data.tar.gz: 0fee9c7b87d626d47ca07919ceafb26ee6b9e4e618d86e1d9c3e4081ad599938
3
+ metadata.gz: 90db5bdbaa4c04377872ec12be70cefa3a0fd9b6ccbb6209582544e3b3d8c087
4
+ data.tar.gz: cad1d32b126fa14b97a7ac688550f3d289c69c93a9a0e3532d127f1c111df5ef
5
5
  SHA512:
6
- metadata.gz: febda260714748526e2e6bad80f102c04b0128ecac9101a384bb1d02e78e634af1a476a24c480b32df49c791572c5d1835a791a1f09aac49144db891c6081559
7
- data.tar.gz: b2488133ade938f776b89dce81add8b7800b8b2da6af07ad206034a867a8b4c33ab16f7a8f997d0d2ed98e48718c5b28f8e37f47a4207acb06d75c091a577203
6
+ metadata.gz: c6365cfcd57941f704095989bb1e1ae5bb39b8aa14d93d5df40346ab87db12fa688de1108887c4009f785cc101c83b5090515117710e9bc7ff9d9dc722c106e3
7
+ data.tar.gz: 8ed81aee751634225e040ec1cca1d942195050ee1734eaab0fa233a66e2eaf096d69c4b238e94d1117f3a97def6c9e20f4189e6d07fe97099df235c1627ad857
data/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ v5.3.0 (August 2026)
2
+ - Fix error handling of the package upload when an invalid template is found
3
+
1
4
  v5.2.0 (June 2026)
2
5
  - No changes
3
6
 
@@ -8,7 +8,7 @@ module Dradis
8
8
 
9
9
  module VERSION
10
10
  MAJOR = 5
11
- MINOR = 2
11
+ MINOR = 3
12
12
  TINY = 0
13
13
  PRE = nil
14
14
 
@@ -46,6 +46,7 @@ module Dradis::Plugins::Projects::Upload
46
46
  options.merge plugin: Dradis::Plugins::Projects::Upload::Template
47
47
  )
48
48
  lookup_table = importer.import(file: template_file)
49
+ raise 'Failed to import the project template file.' unless lookup_table
49
50
  logger.info { 'Done.' }
50
51
 
51
52
 
@@ -12,6 +12,18 @@ module Dradis::Plugins::Projects::Upload
12
12
  class Importer < Dradis::Plugins::Upload::Importer
13
13
  attr_accessor :lookup_table, :template_version
14
14
 
15
+ # libxml2 error codes for isolated, single-character fixups that don't
16
+ # affect document structure (e.g. an invalid byte gets swapped for the
17
+ # unicode replacement character). Safe to recover from and continue.
18
+ #
19
+ # Any other error code (tag mismatches, premature end of data, undefined
20
+ # entities, etc.) indicates the document was actually truncated or
21
+ # corrupted, so those must still cause a hard failure.
22
+ RECOVERABLE_ERROR_CODES = [
23
+ 9, # XML_ERR_INVALID_CHAR
24
+ 81 # XML_ERR_INVALID_ENCODING
25
+ ].freeze
26
+
15
27
  def self.templates
16
28
  { }
17
29
  end
@@ -38,11 +50,16 @@ module Dradis::Plugins::Projects::Upload
38
50
  template = Nokogiri::XML(File.read(params[:file]))
39
51
  logger.info { "Done." }
40
52
 
41
- unless template.errors.empty?
42
- logger.error { "Invalid project template format." }
53
+ unrecoverable_errors = template.errors.reject { |e| RECOVERABLE_ERROR_CODES.include?(e.code) }
54
+
55
+ unless unrecoverable_errors.empty?
56
+ logger.error { "Invalid project template format: #{unrecoverable_errors.map(&:message).join('; ')}" }
43
57
  return false
44
58
  end
45
59
 
60
+ if template.errors.any?
61
+ logger.warn { "The XML parser reported recoverable warnings: #{template.errors.map(&:message).join('; ')}" }
62
+ end
46
63
 
47
64
  if template.xpath('/dradis-template').empty?
48
65
  error = "The uploaded file doesn't look like a Dradis project template (/dradis-template)."
@@ -0,0 +1,19 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <dradis-template version="4">
3
+ <categories></categories>
4
+ <nodes></nodes>
5
+ <issues>
6
+ <issue>
7
+ <id>1</id>
8
+ <author>tester</author>
9
+ <text><![CDATA[#[Title]#
10
+ Sample Issue�
11
+
12
+ #[Description]#
13
+ This is a valid issue used to confirm recoverable XML errors don't block import.
14
+ ]]></text>
15
+ </issue>
16
+ </issues>
17
+ <methodologies></methodologies>
18
+ <tags></tags>
19
+ </dradis-template>
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <dradis-template version="4">
3
+ <categories></categories>
4
+ <nodes>
5
+ <node>
6
+ <id>1</id>
7
+ <label>Node A</label>
8
+ <type-id>0</type-id>
9
+ <parent-id></parent-id>
@@ -0,0 +1,58 @@
1
+ # Run the spec in CE/Pro context with:
2
+ # rspec <relative path to dradis-projects>/spec/lib/dradis/plugins/projects/upload/package_spec.rb
3
+
4
+ require 'rails_helper'
5
+
6
+ describe 'Dradis::Plugins::Projects::Upload::Package::Importer' do
7
+ let(:project) { create(:project) }
8
+ let(:user) { create(:user) }
9
+ let(:importer_class) { Dradis::Plugins::Projects::Upload::Package }
10
+ let(:importer) do
11
+ importer_class::Importer.new(
12
+ default_user_id: user.id,
13
+ plugin: importer_class,
14
+ project_id: project.id
15
+ )
16
+ end
17
+ let(:dir) do
18
+ File.join(File.dirname(__FILE__), '../../../../../', 'fixtures', 'files')
19
+ end
20
+
21
+ context 'when the packaged template has a recoverable XML error' do
22
+ let(:file_path) { File.join(dir, 'package_with_recoverable_error.zip') }
23
+
24
+ it 'imports the package successfully' do
25
+ expect(importer.import(file: file_path)).to be true
26
+ end
27
+
28
+ it 'creates the issue from the package' do
29
+ importer.import(file: file_path)
30
+
31
+ expect(project.issues.count).to eq(1)
32
+ end
33
+ end
34
+
35
+ context 'when the packaged template has an unrecoverable XML error' do
36
+ let(:file_path) { File.join(dir, 'package_with_unrecoverable_error.zip') }
37
+
38
+ it 'returns false instead of raising an unhandled error' do
39
+ expect { importer.import(file: file_path) }.not_to raise_error
40
+ expect(importer.import(file: file_path)).to be false
41
+ end
42
+
43
+ it 'logs a clear error message' do
44
+ logger = double('logger')
45
+ allow(logger).to receive_messages(debug: nil, error: nil, fatal: nil, info: nil, warn: nil)
46
+ expect(logger).to receive(:error).with('Failed to import the project template file.')
47
+
48
+ importer = importer_class::Importer.new(
49
+ default_user_id: user.id,
50
+ logger: logger,
51
+ plugin: importer_class,
52
+ project_id: project.id
53
+ )
54
+
55
+ importer.import(file: file_path)
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,80 @@
1
+ # Run the spec in CE/Pro context with:
2
+ # rspec <relative path to dradis-projects>/spec/lib/dradis/plugins/projects/upload/template_spec.rb
3
+
4
+ require 'rails_helper'
5
+
6
+ describe 'Dradis::Plugins::Projects::Upload::Template::Importer' do
7
+ let(:project) { create(:project) }
8
+ let(:user) { create(:user) }
9
+ let(:importer_class) { Dradis::Plugins::Projects::Upload::Template }
10
+ let(:importer) do
11
+ importer_class::Importer.new(
12
+ default_user_id: user.id,
13
+ plugin: importer_class,
14
+ project_id: project.id
15
+ )
16
+ end
17
+ let(:dir) do
18
+ File.join(File.dirname(__FILE__), '../../../../../', 'fixtures', 'files')
19
+ end
20
+
21
+ context 'uploading a template with a recoverable XML error (e.g. an invalid UTF-8 byte)' do
22
+ let(:file_path) { File.join(dir, 'invalid_byte_recoverable.xml') }
23
+
24
+ it 'still imports the template' do
25
+ expect(importer.import(file: file_path)).not_to be false
26
+ end
27
+
28
+ it 'creates the issue with its content scrubbed instead of being dropped' do
29
+ importer.import(file: file_path)
30
+
31
+ expect(project.issues.count).to eq(1)
32
+ expect(project.issues.first.text).to include('Sample Issue')
33
+ end
34
+
35
+ it 'logs a warning instead of a fatal error' do
36
+ logger = double('logger')
37
+ allow(logger).to receive_messages(debug: nil, error: nil, fatal: nil, info: nil, warn: nil)
38
+ expect(logger).to receive(:warn).at_least(:once)
39
+ expect(logger).not_to receive(:error)
40
+
41
+ importer = importer_class::Importer.new(
42
+ default_user_id: user.id,
43
+ logger: logger,
44
+ plugin: importer_class,
45
+ project_id: project.id
46
+ )
47
+
48
+ importer.import(file: file_path)
49
+ end
50
+ end
51
+
52
+ context 'uploading a template with an unrecoverable XML error (e.g. a truncated file)' do
53
+ let(:file_path) { File.join(dir, 'truncated_unrecoverable.xml') }
54
+
55
+ it 'returns false' do
56
+ expect(importer.import(file: file_path)).to be false
57
+ end
58
+
59
+ it 'does not import anything' do
60
+ importer.import(file: file_path)
61
+
62
+ expect(project.issues.count).to eq(0)
63
+ end
64
+
65
+ it 'logs a fatal error' do
66
+ logger = double('logger')
67
+ allow(logger).to receive_messages(debug: nil, error: nil, fatal: nil, info: nil, warn: nil)
68
+ expect(logger).to receive(:error).at_least(:once)
69
+
70
+ importer = importer_class::Importer.new(
71
+ default_user_id: user.id,
72
+ logger: logger,
73
+ plugin: importer_class,
74
+ project_id: project.id
75
+ )
76
+
77
+ importer.import(file: file_path)
78
+ end
79
+ end
80
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dradis-projects
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.2.0
4
+ version: 5.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Martin
@@ -134,14 +134,20 @@ 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/invalid_byte_recoverable.xml
137
138
  - spec/fixtures/files/malformed_ids.xml
138
139
  - spec/fixtures/files/missing_node.xml
140
+ - spec/fixtures/files/package_with_recoverable_error.zip
141
+ - spec/fixtures/files/package_with_unrecoverable_error.zip
142
+ - spec/fixtures/files/truncated_unrecoverable.xml
139
143
  - spec/fixtures/files/with_comments.xml
140
144
  - spec/fixtures/files/with_invalid_states.xml
141
145
  - spec/fixtures/files/with_states.xml
142
146
  - spec/fixtures/files/with_tags.xml
143
147
  - spec/lib/dradis/plugins/projects/export/v2/template_spec.rb
144
148
  - spec/lib/dradis/plugins/projects/export/v4/template_spec.rb
149
+ - spec/lib/dradis/plugins/projects/upload/package_spec.rb
150
+ - spec/lib/dradis/plugins/projects/upload/template_spec.rb
145
151
  - spec/lib/dradis/plugins/projects/upload/v1/template_spec.rb
146
152
  - spec/lib/dradis/plugins/projects/upload/v2/template_spec.rb
147
153
  - spec/lib/dradis/plugins/projects/upload/v4/template_spec.rb
@@ -168,14 +174,20 @@ specification_version: 4
168
174
  summary: Project export/upload for the Dradis Framework.
169
175
  test_files:
170
176
  - spec/fixtures/files/attachments_url.xml
177
+ - spec/fixtures/files/invalid_byte_recoverable.xml
171
178
  - spec/fixtures/files/malformed_ids.xml
172
179
  - spec/fixtures/files/missing_node.xml
180
+ - spec/fixtures/files/package_with_recoverable_error.zip
181
+ - spec/fixtures/files/package_with_unrecoverable_error.zip
182
+ - spec/fixtures/files/truncated_unrecoverable.xml
173
183
  - spec/fixtures/files/with_comments.xml
174
184
  - spec/fixtures/files/with_invalid_states.xml
175
185
  - spec/fixtures/files/with_states.xml
176
186
  - spec/fixtures/files/with_tags.xml
177
187
  - spec/lib/dradis/plugins/projects/export/v2/template_spec.rb
178
188
  - spec/lib/dradis/plugins/projects/export/v4/template_spec.rb
189
+ - spec/lib/dradis/plugins/projects/upload/package_spec.rb
190
+ - spec/lib/dradis/plugins/projects/upload/template_spec.rb
179
191
  - spec/lib/dradis/plugins/projects/upload/v1/template_spec.rb
180
192
  - spec/lib/dradis/plugins/projects/upload/v2/template_spec.rb
181
193
  - spec/lib/dradis/plugins/projects/upload/v4/template_spec.rb