debian-mirror-gitlab/lib/sbom/package_url/encoder.rb
2023-01-12 18:35:48 +00:00

137 lines
4.4 KiB
Ruby

# 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