Skip to content

Commit

Permalink
Handle null dependencies in package.json (#8212)
Browse files Browse the repository at this point in the history
Our dependency parsing class was parsing these just fine, but not
other classes.
  • Loading branch information
deivid-rodriguez authored Oct 27, 2023
1 parent 1f95aa7 commit 581f084
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 40 deletions.
34 changes: 21 additions & 13 deletions npm_and_yarn/lib/dependabot/npm_and_yarn/file_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@ class FileParser < Dependabot::FileParsers::Base
)?$
}ix

def self.each_dependency(json)
DEPENDENCY_TYPES.each do |type|
deps = json[type] || {}
deps.each do |name, requirement|
yield name, requirement, type
end
end
end

def parse
dependency_set = DependencySet.new
dependency_set += manifest_dependencies
Expand Down Expand Up @@ -64,22 +73,21 @@ def manifest_dependencies
dependency_set = DependencySet.new

package_files.each do |file|
json = JSON.parse(file.content)

# 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
next if json["flat"]

self.class.each_dependency(json) do |name, requirement, type|
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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -562,13 +562,11 @@ def lock_git_deps(content)
return content if git_dependencies_to_lock.empty?

json = JSON.parse(content)
NpmAndYarn::FileParser::DEPENDENCY_TYPES.each do |type|
json.fetch(type, {}).each do |nm, _|
updated_version = git_dependencies_to_lock.dig(nm, :version)
next unless updated_version
NpmAndYarn::FileParser.each_dependency(json) do |nm, _, type|
updated_version = git_dependencies_to_lock.dig(nm, :version)
next unless updated_version

json[type][nm] = git_dependencies_to_lock[nm][:version]
end
json[type][nm] = git_dependencies_to_lock[nm][:version]
end

indent = detect_indentation(content)
Expand Down Expand Up @@ -605,12 +603,10 @@ def git_dependencies_to_lock
def lock_deps_with_latest_reqs(content)
json = JSON.parse(content)

NpmAndYarn::FileParser::DEPENDENCY_TYPES.each do |type|
json.fetch(type, {}).each do |nm, requirement|
next unless requirement == "latest"
NpmAndYarn::FileParser.each_dependency(json) do |nm, requirement, type|
next unless requirement == "latest"

json[type][nm] = "*"
end
json[type][nm] = "*"
end

indent = detect_indentation(content)
Expand Down Expand Up @@ -721,17 +717,15 @@ def restore_locked_package_dependencies(updated_lockfile_content, parsed_updated

dependency_names_to_restore = (dependencies.map(&:name) + git_dependencies_to_lock.keys).uniq

NpmAndYarn::FileParser::DEPENDENCY_TYPES.each do |type|
parsed_package_json.fetch(type, {}).each do |dependency_name, original_requirement|
next unless dependency_names_to_restore.include?(dependency_name)
NpmAndYarn::FileParser.each_dependency(parsed_package_json) do |dependency_name, original_requirement, type|
next unless dependency_names_to_restore.include?(dependency_name)

locked_requirement = parsed_updated_lockfile_content.dig("packages", "", type, dependency_name)
next unless locked_requirement
locked_requirement = parsed_updated_lockfile_content.dig("packages", "", type, dependency_name)
next unless locked_requirement

locked_req = %("#{dependency_name}": "#{locked_requirement}")
original_req = %("#{dependency_name}": "#{original_requirement}")
updated_lockfile_content = updated_lockfile_content.gsub(locked_req, original_req)
end
locked_req = %("#{dependency_name}": "#{locked_requirement}")
original_req = %("#{dependency_name}": "#{original_requirement}")
updated_lockfile_content = updated_lockfile_content.gsub(locked_req, original_req)
end

updated_lockfile_content
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,12 @@ def git_ssh_requirements_to_swap

@git_ssh_requirements_to_swap = []

NpmAndYarn::FileParser::DEPENDENCY_TYPES.each do |t|
JSON.parse(package_json_content).fetch(t, {}).each do |_, req|
next unless req.is_a?(String)
next unless req.start_with?("git+ssh:")
NpmAndYarn::FileParser.each_dependency(JSON.parse(package_json_content)) do |_, req, _t|
next unless req.is_a?(String)
next unless req.start_with?("git+ssh:")

req = req.split("#").first
@git_ssh_requirements_to_swap << req
end
req = req.split("#").first
@git_ssh_requirements_to_swap << req
end

@git_ssh_requirements_to_swap
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# typed: false
# frozen_string_literal: true

require "spec_helper"
require "dependabot/npm_and_yarn/file_updater/package_json_preparer"

RSpec.describe Dependabot::NpmAndYarn::FileUpdater::PackageJsonPreparer do
describe "#prepared_content" do
it "does not craash when finding null dependencies" do
original_content = fixture("projects", "generic", "null_deps", "package.json")

preparer = described_class.new(package_json_content: original_content)

expect(preparer.prepared_content).to eq(original_content)
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "@unction/isnil",
"version": "6.6.3",
"description": "Determines if a value is not a value",
"license": "SEE LICENSE IN LICENSE",
"homepage": "https://github.com/unctionjs/isNil#readme",
"repository": "github:unctionjs/isNil",
"dependencies": null
}

0 comments on commit 581f084

Please sign in to comment.