summaryrefslogtreecommitdiff
path: root/lib/sbom/package_url/argument_validator.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sbom/package_url/argument_validator.rb')
-rw-r--r--lib/sbom/package_url/argument_validator.rb90
1 files changed, 90 insertions, 0 deletions
diff --git a/lib/sbom/package_url/argument_validator.rb b/lib/sbom/package_url/argument_validator.rb
new file mode 100644
index 00000000000..639ee9f89b6
--- /dev/null
+++ b/lib/sbom/package_url/argument_validator.rb
@@ -0,0 +1,90 @@
+# frozen_string_literal: true
+
+module Sbom
+ class PackageUrl
+ class ArgumentValidator
+ QUALIFIER_KEY_REGEXP = /^[A-Za-z\d._-]+$/.freeze
+ START_WITH_NUMBER_REGEXP = /^\d/.freeze
+
+ def initialize(package)
+ @type = package.type
+ @namespace = package.namespace
+ @name = package.name
+ @version = package.version
+ @qualifiers = package.qualifiers
+ @errors = []
+ end
+
+ def validate!
+ validate_type
+ validate_name
+ validate_qualifiers
+ validate_by_type
+
+ raise ArgumentError, formatted_errors if invalid?
+ end
+
+ private
+
+ def invalid?
+ errors.present?
+ end
+
+ attr_reader :type, :namespace, :name, :version, :qualifiers, :errors
+
+ def formatted_errors
+ errors.join(', ')
+ end
+
+ def validate_type
+ errors.push('Type is required') if type.blank?
+ end
+
+ def validate_name
+ errors.push('Name is required') if name.blank?
+ end
+
+ def validate_qualifiers
+ return if qualifiers.nil?
+
+ keys = qualifiers.keys
+ errors.push('Qualifier keys must be unique') unless keys.uniq.size == keys.size
+
+ keys.each do |key|
+ errors.push(key_error(key, 'contains illegal characters')) unless key.match?(QUALIFIER_KEY_REGEXP)
+ errors.push(key_error(key, 'may not start with a number')) if key.match?(START_WITH_NUMBER_REGEXP)
+ end
+ end
+
+ def key_error(key, text)
+ "Qualifier key `#{key}` #{text}"
+ end
+
+ def validate_by_type
+ case type
+ when 'conan'
+ validate_conan
+ when 'cran'
+ validate_cran
+ when 'swift'
+ validate_swift
+ end
+ end
+
+ def validate_conan
+ return unless namespace.blank? ^ (qualifiers.nil? || qualifiers.exclude?('channel'))
+
+ errors.push('Conan packages require the channel be present if published in a namespace and vice-versa')
+ end
+
+ def validate_cran
+ errors.push('Cran packages require a version') if version.blank?
+ end
+
+ def validate_swift
+ errors.push('Swift packages require a namespace') if namespace.blank?
+ errors.push('Swift packages require a version') if version.blank?
+ end
+ end
+ end
+end