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 'lib/sbom/package_url/encoder.rb')
-rw-r--r--lib/sbom/package_url/encoder.rb137
1 files changed, 137 insertions, 0 deletions
diff --git a/lib/sbom/package_url/encoder.rb b/lib/sbom/package_url/encoder.rb
new file mode 100644
index 00000000000..9cf05095571
--- /dev/null
+++ b/lib/sbom/package_url/encoder.rb
@@ -0,0 +1,137 @@
+# frozen_string_literal: true
+
+# MIT License
+#
+# Copyright (c) 2021 package-url
+# Portions Copyright 2022 Gitlab B.V.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+module Sbom
+ class PackageUrl
+ class Encoder
+ include StringUtils
+
+ def initialize(package)
+ @type = package.type
+ @namespace = package.namespace
+ @name = package.name
+ @version = package.version
+ @qualifiers = package.qualifiers
+ @subpath = package.subpath
+ @io = StringIO.new
+ end
+
+ def encode
+ encode_scheme!
+ encode_type!
+ encode_name!
+ encode_version!
+ encode_qualifiers!
+ encode_subpath!
+
+ io.string
+ end
+
+ private
+
+ attr_reader :io
+
+ def encode_scheme!
+ io.write('pkg:')
+ end
+
+ def encode_type!
+ # Append the type string to the purl as a lowercase ASCII string
+ # Append '/' to the purl
+ io.write(@type)
+ io.write('/')
+ end
+
+ def encode_name!
+ # If the namespace is empty:
+ # - Apply type-specific normalization to the name if needed
+ # - UTF-8-encode the name if needed in your programming language
+ # - Append the percent-encoded name to the purl
+ #
+ # If the namespace is not empty:
+ # - Strip the namespace from leading and trailing '/'
+ # - Split on '/' as segments
+ # - Apply type-specific normalization to each segment if needed
+ # - UTF-8-encode each segment if needed in your programming language
+ # - Percent-encode each segment
+ # - Join the segments with '/'
+ # - Append this to the purl
+ # - Append '/' to the purl
+ # - Strip the name from leading and trailing '/'
+ # - Apply type-specific normalization to the name if needed
+ # - UTF-8-encode the name if needed in your programming language
+ # - Append the percent-encoded name to the purl
+ if @namespace.nil?
+ io.write(URI.encode_www_form_component(@name, Encoding::UTF_8))
+ else
+ io.write(encode_segments(@namespace, &:empty?))
+ io.write('/')
+ io.write(URI.encode_www_form_component(strip(@name, '/'), Encoding::UTF_8))
+ end
+ end
+
+ def encode_version!
+ return if @version.nil?
+
+ # - Append '@' to the purl
+ # - UTF-8-encode the version if needed in your programming language
+ # - Append the percent-encoded version to the purl
+ io.write('@')
+ io.write(URI.encode_www_form_component(@version, Encoding::UTF_8))
+ end
+
+ def encode_qualifiers!
+ return if @qualifiers.nil? || encoded_qualifiers.empty?
+
+ io.write('?')
+ io.write(encoded_qualifiers)
+ end
+
+ def encoded_qualifiers
+ @encoded_qualifiers ||= @qualifiers.filter_map do |key, value|
+ next if value.empty?
+
+ next "#{key.downcase}=#{value.join(',')}" if key == 'checksums' && value.is_a?(::Array)
+
+ "#{key.downcase}=#{URI.encode_www_form_component(value, Encoding::UTF_8)}"
+ end.sort.join('&')
+ end
+
+ def encode_subpath!
+ return if @subpath.nil? || encoded_subpath.empty?
+
+ io.write('#')
+ io.write(encoded_subpath)
+ end
+
+ def encoded_subpath
+ @encoded_subpath ||= encode_segments(@subpath) do |segment|
+ # Discard segments which are blank, `.`, or `..`
+ segment.empty? || segment == '.' || segment == '..'
+ end
+ end
+ end
+ end
+end