Module: Homebrew::Vulns::Identify Private
- Defined in:
- vulns/identify.rb
Overview
This module is part of a private API. This module may only be used in the Homebrew/brew repository. Third parties should avoid using this module if possible, as it may be removed or changed without warning.
Derives OSV.dev query keys (forge repo URL, release tag) from formula source URLs. Shared between Scanner and the advisory-matching pipeline.
Defined Under Namespace
Classes: RegistryPackage
Class Method Summary collapse
-
.decode(component) ⇒ String
private
Percent-decode a URL path segment.
-
.gem_name_version(basename) ⇒ Array<([String, nil], [String, nil])>
private
Split a
.gembasename into name and version, discarding any trailing Gem::Platform suffix (e.g.nokogiri-1.16.0-arm64-darwin-22). - .registry_package(url) ⇒ RegistryPackage? private
- .registry_purl(url) ⇒ Array<(String, Purl)>? private
- .repo_url(*urls) ⇒ String? private
- .tag(url) ⇒ String? private
- .version_after_prefix(basename, name) ⇒ String? private
Class Method Details
.decode(component) ⇒ String
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
Percent-decode a URL path segment. Unlike decode_www_form_component
this leaves + alone and unlike decode_uri_component (missing from
Sorbet's stdlib RBI) it never raises on malformed input.
200 201 202 203 204 205 |
# File 'vulns/identify.rb', line 200 def self.decode(component) return component unless component.include?("%") component.b.gsub(/%[0-9A-Fa-f]{2}/) { |m| Integer(m[1, 2], 16).chr } .force_encoding(component.encoding) end |
.gem_name_version(basename) ⇒ Array<([String, nil], [String, nil])>
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
Split a .gem basename into name and version, discarding any trailing
Gem::Platform suffix (e.g. nokogiri-1.16.0-arm64-darwin-22).
219 220 221 222 223 224 225 |
# File 'vulns/identify.rb', line 219 def self.gem_name_version(basename) deplatformed = basename.sub(GEM_PLATFORM_SUFFIX, "") name, sep, version = deplatformed.rpartition("-") return [nil, nil] if sep.empty? || !version.match?(/\A\d[\w.]*\z/) [name, version] end |
.registry_package(url) ⇒ RegistryPackage?
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'vulns/identify.rb', line 118 def self.registry_package(url) return if url.nil? ecosystem, purl = registry_purl(url) return if purl.nil? name = case purl.type when "maven" then "#{purl.namespace}:#{purl.name}" # OSV keys PyPI packages by their PEP 503 normalised name. when "pypi" then purl.name.gsub(/[-_.]+/, "-") # CPANSA is keyed on the distribution name alone, without the author. when "cpan" then purl.name else purl.namespace ? "#{purl.namespace}/#{purl.name}" : purl.name end RegistryPackage.new(ecosystem:, name:, version: purl.version, purl: purl.to_s).freeze end |
.registry_purl(url) ⇒ Array<(String, Purl)>?
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
# File 'vulns/identify.rb', line 136 def self.registry_purl(url) basename = decode(File.basename(url)).sub(ARCHIVE_EXTENSIONS, "") case url when %r{\Ahttps://files\.pythonhosted\.org/packages/(?:[^/]+/){3}(?![^/]+\.whl\z)} # PEP 440 canonical versions contain no hyphen, so the last one delimits. name, _, version = basename.rpartition("-") return if name.empty? ["PyPI", Purl.new(type: "pypi", name:, version:)] when %r{\Ahttps://registry\.npmjs\.org/(?:((?:@|%40)[^/]+)/)?([^/@%][^/]*)/-/} namespace = Regexp.last_match(1) name = T.must(Regexp.last_match(2)) namespace &&= "@#{decode(namespace).delete_prefix("@")}" name = decode(name) return unless (version = version_after_prefix(basename, name)) ["npm", Purl.new(type: "npm", namespace:, name:, version:)] when %r{\Ahttps://static\.crates\.io/crates/([^/]+)/} name = decode(T.must(Regexp.last_match(1))) return unless (version = version_after_prefix(basename, name)) ["crates.io", Purl.new(type: "cargo", name:, version:)] when %r{\Ahttps://rubygems\.org/(?:downloads|gems)/} name, version = gem_name_version(basename) return if name.nil? ["RubyGems", Purl.new(type: "gem", name:, version:)] when %r{\Ahttps://hackage\.haskell\.org/package/([^/]+)} match = T.must(Regexp.last_match(1)).match(HACKAGE_PKGID) return if match.nil? ["Hackage", Purl.new(type: "hackage", name: T.must(match[1]), version: match[2])] when %r{\Ahttps://repo\.hex\.pm/tarballs/} # Hex package names are `[a-z][a-z0-9_]*` so the first hyphen delimits. name, sep, version = basename.partition("-") return if sep.empty? ["Hex", Purl.new(type: "hex", name:, version:)] when %r{/authors/id/[A-Z]/[A-Z]{2}/([A-Z][A-Z0-9-]+)/} = T.must(Regexp.last_match(1)) match = basename.match(CPAN_DISTNAME) return if match.nil? ["CPAN", Purl.new(type: "cpan", namespace: , name: T.must(match[1]), version: match[2])] # Maven Central only: OSV's bare `Maven` ecosystem is Central-scoped, # so third-party repositories (Google, fabricmc, jfrog, ...) are skipped. when %r{\Ahttps://repo1?\.maven\.(?:apache\.)?org/maven2/(.+)/([^/]+)/([^/]+)/\2-\3[.-][^/]+\z}, %r{\Ahttps://search\.maven\.org/remotecontent\?filepath=(.+)/([^/]+)/([^/]+)/\2-\3[.-][^/]+\z} group_id = T.must(Regexp.last_match(1)).tr("/", ".") artifact_id = T.must(Regexp.last_match(2)) version = Regexp.last_match(3) ["Maven", Purl.new(type: "maven", namespace: group_id, name: artifact_id, version:)] when %r{\Ahttps://(?:cran|cloud)\.r-project\.org/src/contrib/(?:Archive/[^/]+/)?([^/_]+)_([^/]+)\.tar\.gz\z} ["CRAN", Purl.new(type: "cran", name: T.must(Regexp.last_match(1)), version: Regexp.last_match(2))] when %r{\Ahttps://(?:api|www)\.nuget\.org/(?:v3-flatcontainer|api/v2/package)/([^/]+)/([^/]+)(?:/|\z)} ["NuGet", Purl.new(type: "nuget", name: T.must(Regexp.last_match(1)), version: Regexp.last_match(2))] end end |
.repo_url(*urls) ⇒ String?
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'vulns/identify.rb', line 58 def self.repo_url(*urls) urls.each do |url| next if url.nil? url = url.sub(WAYBACK_PREFIX, "") FORGES.each do |host, path_pattern| match = url.match(%r{\Ahttps?://#{Regexp.escape(host)}#{path_pattern}}) next if match.nil? repo_path = T.must(match[1]).sub(/\.git$/, "") repo_path = repo_path.downcase if LOWERCASE_PATH_HOSTS.include?(host) return "https://#{host}/#{repo_path}" end end nil end |
.tag(url) ⇒ String?
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
76 77 78 79 80 81 82 83 84 |
# File 'vulns/identify.rb', line 76 def self.tag(url) return if url.nil? TAG_PATTERNS.each do |pattern| match = url.match(pattern) return match[1] if match end nil end |
.version_after_prefix(basename, name) ⇒ String?
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
208 209 210 211 212 213 214 |
# File 'vulns/identify.rb', line 208 def self.version_after_prefix(basename, name) prefix = "#{name}-" return unless basename.start_with?(prefix) version = basename[prefix.length..] version.presence end |