class Dependabot::NpmAndYarn::FileParser
Constants
- DEPENDENCY_TYPES
- GIT_URL_REGEX
Public Instance Methods
parse()
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser.rb, line 34 def parse dependency_set = DependencySet.new dependency_set += manifest_dependencies dependency_set += lockfile_dependencies dependencies = dependency_set.dependencies # TODO: Currently, Dependabot can't handle dependencies that have both # a git source *and* a non-git source. Fix that! dependencies.reject do |dep| git_reqs = dep.requirements.select { |r| r.dig(:source, :type) == "git" } next false if git_reqs.none? next true if git_reqs.map { |r| r.fetch(:source) }.uniq.count > 1 dep.requirements.any? { |r| r.dig(:source, :type) != "git" } end end
Private Instance Methods
alias_package?(requirement)
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser.rb, line 138 def alias_package?(requirement) requirement.start_with?("npm:") end
aliased_package_name?(name)
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser.rb, line 156 def aliased_package_name?(name) name.include?("@npm:") end
build_dependency(file:, type:, name:, requirement:)
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser.rb, line 90 def build_dependency(file:, type:, name:, requirement:) lockfile_details = lockfile_parser.lockfile_details( dependency_name: name, requirement: requirement, manifest_name: file.name ) version = version_for(name, requirement, file.name) return if lockfile_details && !version return if ignore_requirement?(requirement) return if workspace_package_names.include?(name) # TODO: Handle aliased packages: # https://github.com/dependabot/dependabot-core/pull/1115 # # Ignore dependencies with an alias in the name (only supported by Yarn) # Example: "my-fetch-factory@npm:fetch-factory" return if aliased_package_name?(name) Dependency.new( name: name, version: version, package_manager: "npm_and_yarn", requirements: [{ requirement: requirement_for(requirement), file: file.name, groups: [type], source: source_for(name, requirement, file.name) }] ) end
check_required_files()
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser.rb, line 121 def check_required_files raise "No package.json!" unless get_original_file("package.json") end
commit_sha?(string)
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser.rb, line 195 def commit_sha?(string) return false unless string.is_a?(String) string.match?(/^[0-9a-f]{40}$/) end
git_revision_for(name, requirement, manifest_name)
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser.rb, line 179 def git_revision_for(name, requirement, manifest_name) return unless git_url?(requirement) lockfile_details = lockfile_parser.lockfile_details( dependency_name: name, requirement: requirement, manifest_name: manifest_name ) [ lockfile_details&.fetch("version", nil)&.split("#")&.last, lockfile_details&.fetch("resolved", nil)&.split("#")&.last, lockfile_details&.fetch("resolved", nil)&.split("/")&.last ].find { |str| commit_sha?(str) } end
git_source_for(requirement)
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser.rb, line 262 def git_source_for(requirement) details = requirement.match(GIT_URL_REGEX).named_captures prefix = details.fetch("git_prefix") host = if prefix.include?("git@") || prefix.include?("://") prefix.split("git@").last. sub(%r{.*?://}, ""). sub(%r{[:/]$}, ""). split("#").first elsif prefix.include?("bitbucket") then "bitbucket.org" elsif prefix.include?("gitlab") then "gitlab.com" else "github.com" end { type: "git", url: "https://#{host}/#{details['username']}/#{details['repo']}", branch: nil, ref: details["ref"] || "master" } end
git_url?(requirement)
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser.rb, line 146 def git_url?(requirement) requirement.match?(GIT_URL_REGEX) end
git_url_with_semver?(requirement)
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser.rb, line 150 def git_url_with_semver?(requirement) return false unless git_url?(requirement) !requirement.match(GIT_URL_REGEX).named_captures.fetch("semver").nil? end
ignore_requirement?(requirement)
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser.rb, line 125 def ignore_requirement?(requirement) return true if local_path?(requirement) return true if non_git_url?(requirement) # TODO: Handle aliased packages: # https://github.com/dependabot/dependabot-core/pull/1115 alias_package?(requirement) end
local_path?(requirement)
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser.rb, line 134 def local_path?(requirement) requirement.start_with?("link:", "file:", "/", "./", "../", "~/") end
lockfile_dependencies()
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser.rb, line 86 def lockfile_dependencies DependencySet.new(lockfile_parser.parse) end
lockfile_parser()
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser.rb, line 80 def lockfile_parser @lockfile_parser ||= LockfileParser.new( dependency_files: dependency_files ) end
manifest_dependencies()
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser.rb, line 54 def manifest_dependencies dependency_set = DependencySet.new package_files.each do |file| # TODO: Currently, Dependabot can't handle flat dependency files # (and will error at the FileUpdater stage, because the # UpdateChecker doesn't take account of flat resolution). next if JSON.parse(file.content)["flat"] DEPENDENCY_TYPES.each do |type| deps = JSON.parse(file.content)[type] || {} deps.each do |name, requirement| next unless requirement.is_a?(String) requirement = "*" if requirement == "" dep = build_dependency( file: file, type: type, name: name, requirement: requirement ) dependency_set << dep if dep end end end dependency_set end
non_git_url?(requirement)
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser.rb, line 142 def non_git_url?(requirement) requirement.include?("://") && !git_url?(requirement) end
package_files()
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser.rb, line 320 def package_files @package_files ||= begin sub_packages = dependency_files. select { |f| f.name.end_with?("package.json") }. reject { |f| f.name == "package.json" }. reject(&:support_file?) [ dependency_files.find { |f| f.name == "package.json" }, *sub_packages ].compact end end
registry_source_for(resolved_url, name)
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser.rb, line 284 def registry_source_for(resolved_url, name) url = if resolved_url.include?("/~/") # Gemfury format resolved_url.split("/~/").first elsif resolved_url.include?("/#{name}/-/#{name}") # MyGet / Bintray format resolved_url.split("/#{name}/-/#{name}").first. gsub("dl.bintray.com//", "api.bintray.com/npm/"). # GitLab format gsub(%r{\/projects\/\d+}, "") elsif resolved_url.include?("/#{name}/-/#{name.split('/').last}") # Sonatype Nexus / Artifactory JFrog format resolved_url.split("/#{name}/-/#{name.split('/').last}").first elsif (cred_url = url_for_relevant_cred(resolved_url)) then cred_url else resolved_url.split("/")[0..2].join("/") end { type: "registry", url: url } end
requirement_for(requirement)
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser.rb, line 255 def requirement_for(requirement) return requirement unless git_url?(requirement) details = requirement.match(GIT_URL_REGEX).named_captures details["semver"] end
semver_version_for(name, requirement, manifest_name)
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser.rb, line 224 def semver_version_for(name, requirement, manifest_name) lock_version = lockfile_parser.lockfile_details( dependency_name: name, requirement: requirement, manifest_name: manifest_name )&.fetch("version", nil) # This line is to guard against improperly formatted versions in a # lockfile, such as additional characters. NPM/yarn fixes these when # running an update, so we can safely ignore these versions. return unless version_class.correct?(lock_version) lock_version end
source_for(name, requirement, manifest_name)
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser.rb, line 239 def source_for(name, requirement, manifest_name) return git_source_for(requirement) if git_url?(requirement) resolved_url = lockfile_parser.lockfile_details( dependency_name: name, requirement: requirement, manifest_name: manifest_name )&.fetch("resolved", nil) return unless resolved_url return unless resolved_url.start_with?("http") return if resolved_url.match?(/(?<!pkg\.)github/) registry_source_for(resolved_url, name) end
url_for_relevant_cred(resolved_url)
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser.rb, line 305 def url_for_relevant_cred(resolved_url) credential_matching_url = credentials. select { |cred| cred["type"] == "npm_registry" }. sort_by { |cred| cred["registry"].length }. find { |details| resolved_url.include?(details["registry"]) } return unless credential_matching_url # Trim the resolved URL so that it ends at the same point as the # credential registry reg = credential_matching_url["registry"] resolved_url.gsub(/#{Regexp.quote(reg)}.*/, "") + reg end
version_class()
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser.rb, line 336 def version_class NpmAndYarn::Version end
version_for(name, requirement, manifest_name)
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser.rb, line 165 def version_for(name, requirement, manifest_name) if git_url_with_semver?(requirement) semver_version = semver_version_for(name, requirement, manifest_name) return semver_version if semver_version git_revision = git_revision_for(name, requirement, manifest_name) version_from_git_revision(requirement, git_revision) || git_revision elsif git_url?(requirement) git_revision_for(name, requirement, manifest_name) else semver_version_for(name, requirement, manifest_name) end end
version_from_git_revision(requirement, git_revision)
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser.rb, line 201 def version_from_git_revision(requirement, git_revision) tags = Dependabot::GitMetadataFetcher.new( url: git_source_for(requirement).fetch(:url), credentials: credentials ).tags. select { |t| [t.commit_sha, t.tag_sha].include?(git_revision) } tags.each do |t| next unless t.name.match?(Dependabot::GitCommitChecker::VERSION_REGEX) version = t.name.match(Dependabot::GitCommitChecker::VERSION_REGEX). named_captures.fetch("version") next unless version_class.correct?(version) return version end nil rescue Dependabot::GitDependenciesNotReachable nil end
workspace_package_names()
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser.rb, line 160 def workspace_package_names @workspace_package_names ||= package_files.map { |f| JSON.parse(f.content)["name"] }.compact end