class Dependabot::NpmAndYarn::FileParser::LockfileParser
Attributes
dependency_files[R]
Public Class Methods
new(dependency_files:)
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser/lockfile_parser.rb, line 11 def initialize(dependency_files:) @dependency_files = dependency_files end
Public Instance Methods
lockfile_details(dependency_name:, requirement:, manifest_name:)
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser/lockfile_parser.rb, line 23 def lockfile_details(dependency_name:, requirement:, manifest_name:) potential_lockfiles_for_manifest(manifest_name).each do |lockfile| details = if [*package_locks, *shrinkwraps].include?(lockfile) npm_lockfile_details(lockfile, dependency_name, manifest_name) else yarn_lockfile_details(lockfile, dependency_name, requirement, manifest_name) end return details if details end nil end
parse()
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser/lockfile_parser.rb, line 15 def parse dependency_set = Dependabot::NpmAndYarn::FileParser::DependencySet.new dependency_set += yarn_lock_dependencies if yarn_locks.any? dependency_set += package_lock_dependencies if package_locks.any? dependency_set += shrinkwrap_dependencies if shrinkwraps.any? dependency_set.dependencies end
Private Instance Methods
alias_package?(requirement)
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser/lockfile_parser.rb, line 191 def alias_package?(requirement) requirement.include?("@npm:") end
node_modules_path(manifest_name, dependency_name)
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser/lockfile_parser.rb, line 86 def node_modules_path(manifest_name, dependency_name) return "node_modules/#{dependency_name}" if manifest_name == "package.json" workspace_path = manifest_name.gsub("/package.json", "") File.join(workspace_path, "node_modules", dependency_name) end
npm_lockfile_details(lockfile, dependency_name, manifest_name)
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser/lockfile_parser.rb, line 55 def npm_lockfile_details(lockfile, dependency_name, manifest_name) parsed_lockfile = parse_package_lock(lockfile) if Helpers.npm_version(lockfile.content) == "npm7" # NOTE: npm 7 sometimes doesn't install workspace dependencies in the # workspace folder so we need to fallback to checking top-level nested_details = parsed_lockfile.dig("packages", node_modules_path(manifest_name, dependency_name)) details = nested_details || parsed_lockfile.dig("packages", "node_modules/#{dependency_name}") details&.slice("version", "resolved", "integrity", "dev") else parsed_lockfile.dig("dependencies", dependency_name) end end
package_lock_dependencies()
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser/lockfile_parser.rb, line 117 def package_lock_dependencies dependency_set = Dependabot::NpmAndYarn::FileParser::DependencySet.new # NOTE: The DependencySet will de-dupe our dependencies, so they # end up unique by name. That's not a perfect representation of # the nested nature of JS resolution, but it makes everything work # comparably to other flat-resolution strategies package_locks.each do |package_lock| parsed_lockfile = parse_package_lock(package_lock) deps = recursively_fetch_npm_lock_dependencies(parsed_lockfile) dependency_set += deps end dependency_set end
package_locks()
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser/lockfile_parser.rb, line 227 def package_locks @package_locks ||= dependency_files. select { |f| f.name.end_with?("package-lock.json") } end
parse_package_lock(package_lock)
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser/lockfile_parser.rb, line 195 def parse_package_lock(package_lock) @parse_package_lock ||= {} @parse_package_lock[package_lock.name] ||= JSON.parse(package_lock.content) rescue JSON::ParserError raise Dependabot::DependencyFileNotParseable, package_lock.path end
parse_shrinkwrap(shrinkwrap)
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser/lockfile_parser.rb, line 203 def parse_shrinkwrap(shrinkwrap) @parse_shrinkwrap ||= {} @parse_shrinkwrap[shrinkwrap.name] ||= JSON.parse(shrinkwrap.content) rescue JSON::ParserError raise Dependabot::DependencyFileNotParseable, shrinkwrap.path end
parse_yarn_lock(yarn_lock)
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser/lockfile_parser.rb, line 211 def parse_yarn_lock(yarn_lock) @parsed_yarn_lock ||= {} @parsed_yarn_lock[yarn_lock.name] ||= SharedHelpers.in_a_temporary_directory do File.write("yarn.lock", yarn_lock.content) SharedHelpers.run_helper_subprocess( command: NativeHelpers.helper_path, function: "yarn:parseLockfile", args: [Dir.pwd] ) rescue SharedHelpers::HelperSubprocessFailed raise Dependabot::DependencyFileNotParseable, yarn_lock.path end end
potential_lockfiles_for_manifest(manifest_filename)
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser/lockfile_parser.rb, line 42 def potential_lockfiles_for_manifest(manifest_filename) dir_name = File.dirname(manifest_filename) possible_lockfile_names = %w(package-lock.json npm-shrinkwrap.json yarn.lock).map do |f| Pathname.new(File.join(dir_name, f)).cleanpath.to_path end + %w(yarn.lock package-lock.json npm-shrinkwrap.json) possible_lockfile_names.uniq. map { |nm| dependency_files.find { |f| f.name == nm } }. compact end
recursively_fetch_npm_lock_dependencies(object_with_dependencies)
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser/lockfile_parser.rb, line 149 def recursively_fetch_npm_lock_dependencies(object_with_dependencies) dependency_set = Dependabot::NpmAndYarn::FileParser::DependencySet.new object_with_dependencies. fetch("dependencies", {}).each do |name, details| next unless semver_version_for(details["version"]) dependency_args = { name: name, version: semver_version_for(details["version"]), package_manager: "npm_and_yarn", requirements: [] } if details["bundled"] dependency_args[:subdependency_metadata] = [{ npm_bundled: details["bundled"] }] end if details["dev"] dependency_args[:subdependency_metadata] = [{ production: !details["dev"] }] end dependency_set << Dependency.new(**dependency_args) dependency_set += recursively_fetch_npm_lock_dependencies(details) end dependency_set end
semver_version_for(version_string)
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser/lockfile_parser.rb, line 180 def semver_version_for(version_string) # The next two lines are to guard against improperly formatted # versions in a lockfile, such as an empty string or additional # characters. NPM/yarn fixes these when running an update, so we can # safely ignore these versions. return if version_string == "" return unless version_class.correct?(version_string) version_string end
shrinkwrap_dependencies()
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser/lockfile_parser.rb, line 133 def shrinkwrap_dependencies dependency_set = Dependabot::NpmAndYarn::FileParser::DependencySet.new # NOTE: The DependencySet will de-dupe our dependencies, so they # end up unique by name. That's not a perfect representation of # the nested nature of JS resolution, but it makes everything work # comparably to other flat-resolution strategies shrinkwraps.each do |shrinkwrap| parsed_lockfile = parse_shrinkwrap(shrinkwrap) deps = recursively_fetch_npm_lock_dependencies(parsed_lockfile) dependency_set += deps end dependency_set end
shrinkwraps()
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser/lockfile_parser.rb, line 239 def shrinkwraps @shrinkwraps ||= dependency_files. select { |f| f.name.end_with?("npm-shrinkwrap.json") } end
version_class()
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser/lockfile_parser.rb, line 245 def version_class NpmAndYarn::Version end
yarn_lock_dependencies()
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser/lockfile_parser.rb, line 93 def yarn_lock_dependencies dependency_set = Dependabot::NpmAndYarn::FileParser::DependencySet.new yarn_locks.each do |yarn_lock| parse_yarn_lock(yarn_lock).each do |req, details| next unless semver_version_for(details["version"]) next if alias_package?(req) # NOTE: The DependencySet will de-dupe our dependencies, so they # end up unique by name. That's not a perfect representation of # the nested nature of JS resolution, but it makes everything work # comparably to other flat-resolution strategies dependency_set << Dependency.new( name: req.split(/(?<=\w)\@/).first, version: semver_version_for(details["version"]), package_manager: "npm_and_yarn", requirements: [] ) end end dependency_set end
yarn_lockfile_details(lockfile, dependency_name, requirement, _manifest_name)
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser/lockfile_parser.rb, line 69 def yarn_lockfile_details(lockfile, dependency_name, requirement, _manifest_name) parsed_yarn_lock = parse_yarn_lock(lockfile) details_candidates = parsed_yarn_lock. select { |k, _| k.split(/(?<=\w)\@/)[0] == dependency_name } # If there's only one entry for this dependency, use it, even if # the requirement in the lockfile doesn't match if details_candidates.one? details_candidates.first.last else details_candidates.find do |k, _| k.split(/(?<=\w)\@/)[1..-1].join("@") == requirement end&.last end end
yarn_locks()
click to toggle source
# File lib/dependabot/npm_and_yarn/file_parser/lockfile_parser.rb, line 233 def yarn_locks @yarn_locks ||= dependency_files. select { |f| f.name.end_with?("yarn.lock") } end