Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/services/packages/nuget/extract_remote_metadata_file_service.rb')
-rw-r--r--app/services/packages/nuget/extract_remote_metadata_file_service.rb82
1 files changed, 82 insertions, 0 deletions
diff --git a/app/services/packages/nuget/extract_remote_metadata_file_service.rb b/app/services/packages/nuget/extract_remote_metadata_file_service.rb
new file mode 100644
index 00000000000..37624002ce7
--- /dev/null
+++ b/app/services/packages/nuget/extract_remote_metadata_file_service.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+
+module Packages
+ module Nuget
+ class ExtractRemoteMetadataFileService
+ include Gitlab::Utils::StrongMemoize
+
+ ExtractionError = Class.new(StandardError)
+
+ MAX_FILE_SIZE = 4.megabytes.freeze
+ METADATA_FILE_EXTENSION = '.nuspec'
+ MAX_FRAGMENTS = 5 # nuspec file is usually in the first 2 fragments but we buffer 5 max
+
+ def initialize(remote_url)
+ @remote_url = remote_url
+ end
+
+ def execute
+ raise ExtractionError, 'invalid file url' if remote_url.blank?
+
+ if nuspec_file_content.blank? || !nuspec_file_content.instance_of?(String)
+ raise ExtractionError, 'nuspec file not found'
+ end
+
+ ServiceResponse.success(payload: nuspec_file_content)
+ end
+
+ private
+
+ attr_reader :remote_url
+
+ def nuspec_file_content
+ fragments = []
+
+ Gitlab::HTTP.get(remote_url, stream_body: true, allow_object_storage: true) do |fragment|
+ break if fragments.size >= MAX_FRAGMENTS
+
+ fragments << fragment
+ joined_fragments = fragments.join
+
+ next if joined_fragments.exclude?(METADATA_FILE_EXTENSION)
+
+ nuspec_content = extract_nuspec_file(joined_fragments)
+
+ break nuspec_content if nuspec_content.present?
+ end
+ end
+ strong_memoize_attr :nuspec_file_content
+
+ def extract_nuspec_file(fragments)
+ StringIO.open(fragments) do |io|
+ Zip::InputStream.open(io) do |zip|
+ process_zip_entries(zip)
+ end
+ rescue Zip::Error => e
+ raise ExtractionError, "Error opening zip stream: #{e.message}"
+ end
+ end
+
+ def process_zip_entries(zip)
+ while (entry = zip.get_next_entry) # rubocop:disable Lint/AssignmentInCondition
+ next unless entry.name.end_with?(METADATA_FILE_EXTENSION)
+
+ raise ExtractionError, 'nuspec file too big' if entry.size > MAX_FILE_SIZE
+
+ return extract_file_content(entry)
+ end
+ end
+
+ def extract_file_content(entry)
+ Tempfile.create('extract_remote_metadata_file_service') do |file|
+ entry.extract(file.path) { true } # allow #extract to overwrite the file
+ file.read
+ end
+ rescue Zip::DecompressionError
+ '' # Ignore decompression errors and continue reading the next fragment
+ rescue Zip::EntrySizeError => e
+ raise ExtractionError, "nuspec file has the wrong entry size: #{e.message}"
+ end
+ end
+ end
+end