dependabot-npm_and_yarn 0.212.0 → 0.214.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/.eslintrc +1 -1
  3. data/helpers/README.md +2 -2
  4. data/helpers/lib/npm/vulnerability-auditor.js +7 -7
  5. data/helpers/package-lock.json +2781 -2547
  6. data/helpers/package.json +5 -5
  7. data/helpers/test/npm6/fixtures/conflicting-dependency-parser/deeply-nested/package-lock.json +3 -3
  8. data/lib/dependabot/npm_and_yarn/file_fetcher/path_dependency_builder.rb +11 -2
  9. data/lib/dependabot/npm_and_yarn/file_fetcher.rb +90 -5
  10. data/lib/dependabot/npm_and_yarn/file_parser/lockfile_parser.rb +15 -4
  11. data/lib/dependabot/npm_and_yarn/file_parser.rb +15 -6
  12. data/lib/dependabot/npm_and_yarn/file_updater/npm_lockfile_updater.rb +35 -21
  13. data/lib/dependabot/npm_and_yarn/file_updater/npmrc_builder.rb +86 -7
  14. data/lib/dependabot/npm_and_yarn/file_updater/package_json_updater.rb +2 -2
  15. data/lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb +96 -32
  16. data/lib/dependabot/npm_and_yarn/file_updater.rb +53 -1
  17. data/lib/dependabot/npm_and_yarn/helpers.rb +94 -0
  18. data/lib/dependabot/npm_and_yarn/package_name.rb +2 -2
  19. data/lib/dependabot/npm_and_yarn/requirement.rb +3 -3
  20. data/lib/dependabot/npm_and_yarn/update_checker/dependency_files_builder.rb +43 -1
  21. data/lib/dependabot/npm_and_yarn/update_checker/latest_version_finder.rb +13 -14
  22. data/lib/dependabot/npm_and_yarn/update_checker/library_detector.rb +16 -3
  23. data/lib/dependabot/npm_and_yarn/update_checker/registry_finder.rb +77 -23
  24. data/lib/dependabot/npm_and_yarn/update_checker/requirements_updater.rb +3 -4
  25. data/lib/dependabot/npm_and_yarn/update_checker/subdependency_version_resolver.rb +19 -4
  26. data/lib/dependabot/npm_and_yarn/update_checker/version_resolver.rb +74 -30
  27. data/lib/dependabot/npm_and_yarn/update_checker/vulnerability_auditor.rb +33 -8
  28. data/lib/dependabot/npm_and_yarn/update_checker.rb +76 -21
  29. data/lib/dependabot/npm_and_yarn/version.rb +1 -1
  30. data/lib/dependabot/npm_and_yarn.rb +2 -0
  31. metadata +13 -56
  32. data/lib/dependabot/npm_and_yarn/file_parser/yarn_lockfile_parser.rb +0 -59
@@ -4,6 +4,7 @@ require "uri"
4
4
 
5
5
  require "dependabot/npm_and_yarn/file_updater"
6
6
  require "dependabot/npm_and_yarn/file_parser"
7
+ require "dependabot/npm_and_yarn/helpers"
7
8
  require "dependabot/npm_and_yarn/update_checker/registry_finder"
8
9
  require "dependabot/npm_and_yarn/native_helpers"
9
10
  require "dependabot/shared_helpers"
@@ -17,9 +18,10 @@ module Dependabot
17
18
  require_relative "npmrc_builder"
18
19
  require_relative "package_json_updater"
19
20
 
20
- def initialize(dependencies:, dependency_files:, credentials:)
21
+ def initialize(dependencies:, dependency_files:, repo_contents_path:, credentials:)
21
22
  @dependencies = dependencies
22
23
  @dependency_files = dependency_files
24
+ @repo_contents_path = repo_contents_path
23
25
  @credentials = credentials
24
26
  end
25
27
 
@@ -35,12 +37,11 @@ module Dependabot
35
37
 
36
38
  private
37
39
 
38
- attr_reader :dependencies, :dependency_files, :credentials
40
+ attr_reader :dependencies, :dependency_files, :repo_contents_path, :credentials
39
41
 
40
- UNREACHABLE_GIT = /ls-remote --tags --heads (?<url>.*)/.freeze
41
- TIMEOUT_FETCHING_PACKAGE =
42
- %r{(?<url>.+)/(?<package>[^/]+): ETIMEDOUT}.freeze
43
- INVALID_PACKAGE = /Can't add "(?<package_req>.*)": invalid/.freeze
42
+ UNREACHABLE_GIT = /ls-remote --tags --heads (?<url>.*)/
43
+ TIMEOUT_FETCHING_PACKAGE = %r{(?<url>.+)/(?<package>[^/]+): ETIMEDOUT}
44
+ INVALID_PACKAGE = /Can't add "(?<package_req>.*)": invalid/
44
45
 
45
46
  def top_level_dependencies
46
47
  dependencies.select(&:top_level?)
@@ -51,13 +52,14 @@ module Dependabot
51
52
  end
52
53
 
53
54
  def updated_yarn_lock(yarn_lock)
54
- SharedHelpers.in_a_temporary_directory do
55
- write_temporary_dependency_files
55
+ base_dir = dependency_files.first.directory
56
+ SharedHelpers.in_a_temporary_repo_directory(base_dir, repo_contents_path) do
57
+ write_temporary_dependency_files(yarn_lock)
56
58
  lockfile_name = Pathname.new(yarn_lock.name).basename.to_s
57
59
  path = Pathname.new(yarn_lock.name).dirname.to_s
58
60
  updated_files = run_current_yarn_update(
59
61
  path: path,
60
- lockfile_name: lockfile_name
62
+ yarn_lock: yarn_lock
61
63
  )
62
64
  updated_files.fetch(lockfile_name)
63
65
  end
@@ -65,7 +67,7 @@ module Dependabot
65
67
  handle_yarn_lock_updater_error(e, yarn_lock)
66
68
  end
67
69
 
68
- def run_current_yarn_update(path:, lockfile_name:)
70
+ def run_current_yarn_update(path:, yarn_lock:)
69
71
  top_level_dependency_updates = top_level_dependencies.map do |d|
70
72
  {
71
73
  name: d.name,
@@ -76,12 +78,12 @@ module Dependabot
76
78
 
77
79
  run_yarn_updater(
78
80
  path: path,
79
- lockfile_name: lockfile_name,
81
+ yarn_lock: yarn_lock,
80
82
  top_level_dependency_updates: top_level_dependency_updates
81
83
  )
82
84
  end
83
85
 
84
- def run_previous_yarn_update(path:, lockfile_name:)
86
+ def run_previous_yarn_update(path:, yarn_lock:)
85
87
  previous_top_level_dependencies = top_level_dependencies.map do |d|
86
88
  {
87
89
  name: d.name,
@@ -94,22 +96,29 @@ module Dependabot
94
96
 
95
97
  run_yarn_updater(
96
98
  path: path,
97
- lockfile_name: lockfile_name,
99
+ yarn_lock: yarn_lock,
98
100
  top_level_dependency_updates: previous_top_level_dependencies
99
101
  )
100
102
  end
101
103
 
102
104
  # rubocop:disable Metrics/PerceivedComplexity
103
- def run_yarn_updater(path:, lockfile_name:,
104
- top_level_dependency_updates:)
105
+ def run_yarn_updater(path:, yarn_lock:, top_level_dependency_updates:)
105
106
  SharedHelpers.with_git_configured(credentials: credentials) do
106
107
  Dir.chdir(path) do
107
108
  if top_level_dependency_updates.any?
108
- run_yarn_top_level_updater(
109
- top_level_dependency_updates: top_level_dependency_updates
110
- )
109
+ if Helpers.yarn_berry?(yarn_lock)
110
+ run_yarn_berry_top_level_updater(top_level_dependency_updates: top_level_dependency_updates,
111
+ yarn_lock: yarn_lock)
112
+ else
113
+
114
+ run_yarn_top_level_updater(
115
+ top_level_dependency_updates: top_level_dependency_updates
116
+ )
117
+ end
118
+ elsif Helpers.yarn_berry?(yarn_lock)
119
+ run_yarn_berry_subdependency_updater(yarn_lock: yarn_lock)
111
120
  else
112
- run_yarn_subdependency_updater(lockfile_name: lockfile_name)
121
+ run_yarn_subdependency_updater(yarn_lock: yarn_lock)
113
122
  end
114
123
  end
115
124
  end
@@ -133,6 +142,43 @@ module Dependabot
133
142
 
134
143
  # rubocop:enable Metrics/PerceivedComplexity
135
144
 
145
+ def run_yarn_berry_top_level_updater(top_level_dependency_updates:, yarn_lock:)
146
+ write_temporary_dependency_files(yarn_lock)
147
+ # If the requirements have changed, it means we've updated the
148
+ # package.json file(s), and we can just run yarn install to get the
149
+ # lockfile in the right state. Otherwise we'll need to manually update
150
+ # the lockfile.
151
+
152
+ command = if top_level_dependency_updates.all? { |dep| requirements_changed?(dep[:name]) }
153
+ "yarn install #{Helpers.yarn_berry_args}".strip
154
+ else
155
+ updates = top_level_dependency_updates.collect do |dep|
156
+ dep[:name]
157
+ end
158
+
159
+ "yarn up -R #{updates.join(' ')} #{Helpers.yarn_berry_args}".strip
160
+ end
161
+ Helpers.run_yarn_commands(command)
162
+ { yarn_lock.name => File.read(yarn_lock.name) }
163
+ end
164
+
165
+ def requirements_changed?(dependency_name)
166
+ dep = top_level_dependencies.first { |d| d.name == dependency_name }
167
+ dep.requirements != dep.previous_requirements
168
+ end
169
+
170
+ def run_yarn_berry_subdependency_updater(yarn_lock:)
171
+ dep = sub_dependencies.first
172
+ update = "#{dep.name}@#{dep.version}"
173
+
174
+ Helpers.run_yarn_commands(
175
+ "yarn add #{update} #{Helpers.yarn_berry_args}".strip,
176
+ "yarn dedupe #{dep.name} #{Helpers.yarn_berry_args}".strip,
177
+ "yarn remove #{dep.name} #{Helpers.yarn_berry_args}".strip
178
+ )
179
+ { yarn_lock.name => File.read(yarn_lock.name) }
180
+ end
181
+
136
182
  def run_yarn_top_level_updater(top_level_dependency_updates:)
137
183
  SharedHelpers.run_helper_subprocess(
138
184
  command: NativeHelpers.helper_path,
@@ -144,7 +190,8 @@ module Dependabot
144
190
  )
145
191
  end
146
192
 
147
- def run_yarn_subdependency_updater(lockfile_name:)
193
+ def run_yarn_subdependency_updater(yarn_lock:)
194
+ lockfile_name = Pathname.new(yarn_lock.name).basename.to_s
148
195
  SharedHelpers.run_helper_subprocess(
149
196
  command: NativeHelpers.helper_path,
150
197
  function: "yarn:updateSubdependency",
@@ -259,12 +306,11 @@ module Dependabot
259
306
 
260
307
  @resolvable_before_update[yarn_lock.name] =
261
308
  begin
262
- SharedHelpers.in_a_temporary_directory do
263
- write_temporary_dependency_files(update_package_json: false)
264
- lockfile_name = Pathname.new(yarn_lock.name).basename.to_s
309
+ base_dir = dependency_files.first.directory
310
+ SharedHelpers.in_a_temporary_repo_directory(base_dir, repo_contents_path) do
311
+ write_temporary_dependency_files(yarn_lock, update_package_json: false)
265
312
  path = Pathname.new(yarn_lock.name).dirname.to_s
266
- run_previous_yarn_update(path: path,
267
- lockfile_name: lockfile_name)
313
+ run_previous_yarn_update(path: path, yarn_lock: yarn_lock)
268
314
  end
269
315
 
270
316
  true
@@ -282,11 +328,15 @@ module Dependabot
282
328
  end
283
329
  end
284
330
 
285
- def write_temporary_dependency_files(update_package_json: true)
331
+ def write_temporary_dependency_files(yarn_lock, update_package_json: true)
286
332
  write_lockfiles
287
333
 
288
- File.write(".npmrc", npmrc_content)
289
- File.write(".yarnrc", yarnrc_content) if yarnrc_specifies_npm_reg?
334
+ if Helpers.yarn_berry?(yarn_lock)
335
+ File.write(".yarnrc.yml", yarnrc_yml_content) if yarnrc_yml_file
336
+ else
337
+ File.write(".npmrc", npmrc_content) unless Helpers.yarn_berry?(yarn_lock)
338
+ File.write(".yarnrc", yarnrc_content) if yarnrc_specifies_private_reg?
339
+ end
290
340
 
291
341
  package_files.each do |file|
292
342
  path = file.name
@@ -420,7 +470,8 @@ module Dependabot
420
470
  dependency: missing_dep,
421
471
  credentials: credentials,
422
472
  npmrc_file: npmrc_file,
423
- yarnrc_file: yarnrc_file
473
+ yarnrc_file: yarnrc_file,
474
+ yarnrc_yml_file: yarnrc_yml_file
424
475
  ).registry
425
476
 
426
477
  return if UpdateChecker::RegistryFinder.central_registry?(reg) && !package_name.start_with?("@")
@@ -471,7 +522,7 @@ module Dependabot
471
522
  npmrc_content.match?(/^package-lock\s*=\s*false/)
472
523
  end
473
524
 
474
- def yarnrc_specifies_npm_reg?
525
+ def yarnrc_specifies_private_reg?
475
526
  return false unless yarnrc_file
476
527
 
477
528
  regex = UpdateChecker::RegistryFinder::YARN_GLOBAL_REGISTRY_REGEX
@@ -484,11 +535,16 @@ module Dependabot
484
535
 
485
536
  return false unless yarnrc_global_registry
486
537
 
487
- URI(yarnrc_global_registry).host == "registry.npmjs.org"
538
+ UpdateChecker::RegistryFinder::CENTRAL_REGISTRIES.any? do |r|
539
+ r.include?(URI(yarnrc_global_registry).host)
540
+ end
488
541
  end
489
542
 
490
543
  def yarnrc_content
491
- 'registry "https://registry.npmjs.org"'
544
+ NpmrcBuilder.new(
545
+ credentials: credentials,
546
+ dependency_files: dependency_files
547
+ ).yarnrc_content
492
548
  end
493
549
 
494
550
  def sanitized_package_json_content(content)
@@ -524,6 +580,14 @@ module Dependabot
524
580
  def npmrc_file
525
581
  dependency_files.find { |f| f.name == ".npmrc" }
526
582
  end
583
+
584
+ def yarnrc_yml_file
585
+ dependency_files.find { |f| f.name.end_with?(".yarnrc.yml") }
586
+ end
587
+
588
+ def yarnrc_yml_content
589
+ yarnrc_yml_file.content
590
+ end
527
591
  end
528
592
  end
529
593
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "dependabot/file_updaters"
4
4
  require "dependabot/file_updaters/base"
5
+ require "dependabot/file_updaters/vendor_updater"
5
6
  require "dependabot/npm_and_yarn/dependency_files_filterer"
6
7
  require "dependabot/npm_and_yarn/sub_dependency_files_filterer"
7
8
 
@@ -53,11 +54,61 @@ module Dependabot
53
54
  )
54
55
  end
55
56
 
56
- updated_files
57
+ vendor_updated_files(updated_files)
57
58
  end
58
59
 
59
60
  private
60
61
 
62
+ def vendor_updated_files(updated_files)
63
+ base_dir = updated_files.first.directory
64
+ pnp_updater.updated_vendor_cache_files(base_directory: base_dir).each do |file|
65
+ updated_files << file if file.name == ".pnp.cjs" || file.name == ".pnp.data.json"
66
+ end
67
+ # updated .pnp.cjs means zero install, include cache
68
+ if updated_files.find { |f| f.name == ".pnp.cjs" }
69
+ vendor_updater.updated_vendor_cache_files(base_directory: base_dir).each { |file| updated_files << file }
70
+ end
71
+ install_state_updater.updated_vendor_cache_files(base_directory: base_dir).each do |file|
72
+ updated_files << file
73
+ end
74
+
75
+ updated_files
76
+ end
77
+
78
+ # Dynamically fetch the vendor cache folder from yarn
79
+ def vendor_cache_dir
80
+ return @vendor_cache_dir if defined?(@vendor_cache_dir)
81
+
82
+ @vendor_cache_dir = Helpers.fetch_yarnrc_yml_value("cacheFolder", "./.yarn/cache")
83
+ end
84
+
85
+ def install_state_path
86
+ return @install_state_path if defined?(@install_state_path)
87
+
88
+ @install_state_path = Helpers.fetch_yarnrc_yml_value("installStatePath", "./.yarn/install-state.gz")
89
+ end
90
+
91
+ def vendor_updater
92
+ Dependabot::FileUpdaters::VendorUpdater.new(
93
+ repo_contents_path: repo_contents_path,
94
+ vendor_dir: vendor_cache_dir
95
+ )
96
+ end
97
+
98
+ def install_state_updater
99
+ Dependabot::FileUpdaters::VendorUpdater.new(
100
+ repo_contents_path: repo_contents_path,
101
+ vendor_dir: install_state_path
102
+ )
103
+ end
104
+
105
+ def pnp_updater
106
+ Dependabot::FileUpdaters::VendorUpdater.new(
107
+ repo_contents_path: repo_contents_path,
108
+ vendor_dir: "./"
109
+ )
110
+ end
111
+
61
112
  def filtered_dependency_files
62
113
  @filtered_dependency_files ||=
63
114
  if dependencies.select(&:top_level?).any?
@@ -175,6 +226,7 @@ module Dependabot
175
226
  YarnLockfileUpdater.new(
176
227
  dependencies: dependencies,
177
228
  dependency_files: dependency_files,
229
+ repo_contents_path: repo_contents_path,
178
230
  credentials: credentials
179
231
  )
180
232
  end
@@ -15,6 +15,100 @@ module Dependabot
15
15
  rescue JSON::ParserError
16
16
  6
17
17
  end
18
+
19
+ def self.fetch_yarnrc_yml_value(key, default_value)
20
+ if File.exist?(".yarnrc.yml") && (yarnrc = YAML.load_file(".yarnrc.yml"))
21
+ yarnrc.fetch(key, default_value)
22
+ else
23
+ default_value
24
+ end
25
+ end
26
+
27
+ def self.yarn_berry?(yarn_lock)
28
+ yaml = YAML.safe_load(yarn_lock.content)
29
+ yaml.key?("__metadata")
30
+ rescue StandardError
31
+ false
32
+ end
33
+
34
+ def self.yarn_major_version
35
+ output = SharedHelpers.run_shell_command("yarn --version")
36
+ Version.new(output).major
37
+ end
38
+
39
+ def self.yarn_zero_install?
40
+ File.exist?(".pnp.cjs")
41
+ end
42
+
43
+ def self.yarn_berry_args
44
+ if yarn_major_version == 2
45
+ ""
46
+ elsif yarn_major_version >= 3 && yarn_zero_install?
47
+ "--mode=skip-build"
48
+ else
49
+ "--mode=update-lockfile"
50
+ end
51
+ end
52
+
53
+ def self.setup_yarn_berry
54
+ # Always disable immutable installs so yarn's CI detection doesn't prevent updates.
55
+ SharedHelpers.run_shell_command("yarn config set enableImmutableInstalls false")
56
+ # We never want to execute postinstall scripts, either set this config or mode=skip-build must be set
57
+ if yarn_major_version == 2 || !yarn_zero_install?
58
+ SharedHelpers.run_shell_command("yarn config set enableScripts false")
59
+ end
60
+ if (http_proxy = ENV.fetch("HTTP_PROXY", false))
61
+ SharedHelpers.run_shell_command("yarn config set httpProxy #{http_proxy}")
62
+ end
63
+ if (https_proxy = ENV.fetch("HTTPS_PROXY", false))
64
+ SharedHelpers.run_shell_command("yarn config set httpsProxy #{https_proxy}")
65
+ end
66
+ return unless (ca_file_path = ENV.fetch("NODE_EXTRA_CA_CERTS", false))
67
+
68
+ if yarn_major_version >= 4
69
+ SharedHelpers.run_shell_command("yarn config set httpsCaFilePath #{ca_file_path}")
70
+ else
71
+ SharedHelpers.run_shell_command("yarn config set caFilePath #{ca_file_path}")
72
+ end
73
+ end
74
+
75
+ # Run any number of yarn commands while ensuring that `enableScripts` is
76
+ # set to false. Yarn commands should _not_ be ran outside of this helper
77
+ # to ensure that postinstall scripts are never executed, as they could
78
+ # contain malicious code.
79
+ def self.run_yarn_commands(*commands)
80
+ setup_yarn_berry
81
+ commands.each { |cmd| SharedHelpers.run_shell_command(cmd) }
82
+ end
83
+
84
+ # Run a single yarn command returning stdout/stderr
85
+ def self.run_yarn_command(command)
86
+ setup_yarn_berry
87
+ SharedHelpers.run_shell_command(command)
88
+ end
89
+
90
+ def self.dependencies_with_all_versions_metadata(dependency_set)
91
+ working_set = Dependabot::NpmAndYarn::FileParser::DependencySet.new
92
+ dependencies = []
93
+
94
+ names = dependency_set.dependencies.map(&:name)
95
+ names.each do |name|
96
+ all_versions = dependency_set.all_versions_for_name(name)
97
+ all_versions.each do |dep|
98
+ metadata_versions = dep.metadata.fetch(:all_versions, [])
99
+ if metadata_versions.any?
100
+ metadata_versions.each { |a| working_set << a }
101
+ else
102
+ working_set << dep
103
+ end
104
+ end
105
+ dependency = working_set.dependency_for_name(name)
106
+ dependency.metadata[:all_versions] = working_set.all_versions_for_name(name)
107
+ dependencies << dependency
108
+ end
109
+
110
+ dependencies
111
+ end
18
112
  end
19
113
  end
20
114
  end
@@ -18,7 +18,7 @@ module Dependabot
18
18
  [a-z0-9\-\_\.\!\~\*\'\(\)]+ # URL-safe characters
19
19
  )
20
20
  \z # end of string
21
- }xi.freeze # multi-line/case-insensitive
21
+ }xi # multi-line/case-insensitive
22
22
 
23
23
  TYPES_PACKAGE_NAME_REGEX = %r{
24
24
  \A # beginning of string
@@ -26,7 +26,7 @@ module Dependabot
26
26
  ((?<scope>.+)__)? # capture scope
27
27
  (?<name>.+) # capture name
28
28
  \z # end of string
29
- }xi.freeze # multi-line/case-insensitive
29
+ }xi # multi-line/case-insensitive
30
30
 
31
31
  class InvalidPackageName < StandardError; end
32
32
 
@@ -6,8 +6,8 @@ require "dependabot/npm_and_yarn/version"
6
6
  module Dependabot
7
7
  module NpmAndYarn
8
8
  class Requirement < Gem::Requirement
9
- AND_SEPARATOR = /(?<=[a-zA-Z0-9*])\s+(?:&+\s+)?(?!\s*[|-])/.freeze
10
- OR_SEPARATOR = /(?<=[a-zA-Z0-9*])\s*\|+/.freeze
9
+ AND_SEPARATOR = /(?<=[a-zA-Z0-9*])\s+(?:&+\s+)?(?!\s*[|-])/
10
+ OR_SEPARATOR = /(?<=[a-zA-Z0-9*])\s*\|+/
11
11
  LATEST_REQUIREMENT = "latest"
12
12
 
13
13
  # Override the version pattern to allow a 'v' prefix
@@ -15,7 +15,7 @@ module Dependabot
15
15
  version_pattern = "v?#{NpmAndYarn::Version::VERSION_PATTERN}"
16
16
 
17
17
  PATTERN_RAW = "\\s*(#{quoted})?\\s*(#{version_pattern})\\s*"
18
- PATTERN = /\A#{PATTERN_RAW}\z/.freeze
18
+ PATTERN = /\A#{PATTERN_RAW}\z/
19
19
 
20
20
  def self.parse(obj)
21
21
  return ["=", nil] if obj.is_a?(String) && obj.strip == LATEST_REQUIREMENT
@@ -16,7 +16,12 @@ module Dependabot
16
16
  def write_temporary_dependency_files
17
17
  write_lock_files
18
18
 
19
- File.write(".npmrc", npmrc_content)
19
+ if Helpers.yarn_berry?(yarn_locks.first)
20
+ File.write(".yarnrc.yml", yarnrc_yml_content) if yarnrc_yml_file
21
+ else
22
+ File.write(".npmrc", npmrc_content) unless Helpers.yarn_berry?(yarn_locks.first)
23
+ File.write(".yarnrc", yarnrc_content) if yarnrc_specifies_private_reg?
24
+ end
20
25
 
21
26
  package_files.each do |file|
22
27
  path = file.name
@@ -69,6 +74,24 @@ module Dependabot
69
74
  end
70
75
  end
71
76
 
77
+ def yarnrc_specifies_private_reg?
78
+ return false unless yarnrc_file
79
+
80
+ regex = UpdateChecker::RegistryFinder::YARN_GLOBAL_REGISTRY_REGEX
81
+ yarnrc_global_registry =
82
+ yarnrc_file.content.
83
+ lines.find { |line| line.match?(regex) }&.
84
+ match(regex)&.
85
+ named_captures&.
86
+ fetch("registry")
87
+
88
+ return false unless yarnrc_global_registry
89
+
90
+ UpdateChecker::RegistryFinder::CENTRAL_REGISTRIES.none? do |r|
91
+ r.include?(URI(yarnrc_global_registry).host)
92
+ end
93
+ end
94
+
72
95
  # Duplicated in NpmLockfileUpdater
73
96
  # Remove the dependency we want to update from the lockfile and let
74
97
  # yarn find the latest resolvable version and fix the lockfile
@@ -88,6 +111,25 @@ module Dependabot
88
111
  dependency_files: dependency_files
89
112
  ).npmrc_content
90
113
  end
114
+
115
+ def yarnrc_file
116
+ dependency_files.find { |f| f.name == ".yarnrc" }
117
+ end
118
+
119
+ def yarnrc_content
120
+ NpmAndYarn::FileUpdater::NpmrcBuilder.new(
121
+ credentials: credentials,
122
+ dependency_files: dependency_files
123
+ ).yarnrc_content
124
+ end
125
+
126
+ def yarnrc_yml_file
127
+ dependency_files.find { |f| f.name.end_with?(".yarnrc.yml") }
128
+ end
129
+
130
+ def yarnrc_yml_content
131
+ yarnrc_yml_file.content
132
+ end
91
133
  end
92
134
  end
93
135
  end
@@ -130,10 +130,10 @@ module Dependabot
130
130
  end
131
131
 
132
132
  def filter_lower_versions(versions_array)
133
- return versions_array unless dependency.version && version_class.correct?(dependency.version)
133
+ return versions_array unless dependency.numeric_version
134
134
 
135
135
  versions_array.
136
- select { |version, _| version > version_class.new(dependency.version) }
136
+ select { |version, _| version > dependency.numeric_version }
137
137
  end
138
138
 
139
139
  def version_from_dist_tags
@@ -159,13 +159,10 @@ module Dependabot
159
159
  wants_latest_dist_tag?(latest) ? latest : nil
160
160
  end
161
161
 
162
- # rubocop:disable Metrics/PerceivedComplexity
163
162
  def related_to_current_pre?(version)
164
- current_version = dependency.version
165
- if current_version &&
166
- version_class.correct?(current_version) &&
167
- version_class.new(current_version).prerelease? &&
168
- version_class.new(current_version).release == version.release
163
+ current_version = dependency.numeric_version
164
+ if current_version&.prerelease? &&
165
+ current_version&.release == version.release
169
166
  return true
170
167
  end
171
168
 
@@ -181,7 +178,6 @@ module Dependabot
181
178
  false
182
179
  end
183
180
  end
184
- # rubocop:enable Metrics/PerceivedComplexity
185
181
 
186
182
  def specified_dist_tag_requirement?
187
183
  dependency.requirements.any? do |req|
@@ -204,10 +200,9 @@ module Dependabot
204
200
  end
205
201
 
206
202
  def current_version_greater_than?(version)
207
- return false unless dependency.version
208
- return false unless version_class.correct?(dependency.version)
203
+ return false unless dependency.numeric_version
209
204
 
210
- version_class.new(dependency.version) > version
205
+ dependency.numeric_version > version
211
206
  end
212
207
 
213
208
  def current_requirement_greater_than?(version)
@@ -292,7 +287,6 @@ module Dependabot
292
287
  url: dependency_url,
293
288
  headers: registry_auth_headers
294
289
  )
295
-
296
290
  return response unless response.status == 500
297
291
  return response unless registry_auth_headers["Authorization"]
298
292
 
@@ -371,7 +365,8 @@ module Dependabot
371
365
  dependency: dependency,
372
366
  credentials: credentials,
373
367
  npmrc_file: npmrc_file,
374
- yarnrc_file: yarnrc_file
368
+ yarnrc_file: yarnrc_file,
369
+ yarnrc_yml_file: yarnrc_yml_file
375
370
  )
376
371
  end
377
372
 
@@ -395,6 +390,10 @@ module Dependabot
395
390
  dependency_files.find { |f| f.name.end_with?(".yarnrc") }
396
391
  end
397
392
 
393
+ def yarnrc_yml_file
394
+ dependency_files.find { |f| f.name.end_with?(".yarnrc.yml") }
395
+ end
396
+
398
397
  # TODO: Remove need for me
399
398
  def git_dependency?
400
399
  # ignored_version/raise_on_ignored are irrelevant.
@@ -8,8 +8,10 @@ module Dependabot
8
8
  module NpmAndYarn
9
9
  class UpdateChecker
10
10
  class LibraryDetector
11
- def initialize(package_json_file:)
11
+ def initialize(package_json_file:, credentials:, dependency_files:)
12
12
  @package_json_file = package_json_file
13
+ @credentials = credentials
14
+ @dependency_files = dependency_files
13
15
  end
14
16
 
15
17
  def library?
@@ -20,7 +22,7 @@ module Dependabot
20
22
 
21
23
  private
22
24
 
23
- attr_reader :package_json_file
25
+ attr_reader :package_json_file, :credentials, :dependency_files
24
26
 
25
27
  def package_json_may_be_for_library?
26
28
  return false unless project_name
@@ -36,7 +38,8 @@ module Dependabot
36
38
  return false unless project_description
37
39
 
38
40
  # Check if the project is listed on npm. If it is, it's a library
39
- @project_npm_response ||= Dependabot::RegistryClient.get(url: "https://registry.npmjs.org/#{escaped_project_name}")
41
+ url = "#{registry.chomp('/')}/#{escaped_project_name}"
42
+ @project_npm_response ||= Dependabot::RegistryClient.get(url: url)
40
43
  return false unless @project_npm_response.status == 200
41
44
 
42
45
  @project_npm_response.body.force_encoding("UTF-8").encode.
@@ -56,6 +59,16 @@ module Dependabot
56
59
  def parsed_package_json
57
60
  @parsed_package_json ||= JSON.parse(package_json_file.content)
58
61
  end
62
+
63
+ def registry
64
+ NpmAndYarn::UpdateChecker::RegistryFinder.new(
65
+ dependency: nil,
66
+ credentials: credentials,
67
+ npmrc_file: dependency_files.find { |f| f.name.end_with?(".npmrc") },
68
+ yarnrc_file: dependency_files.find { |f| f.name.end_with?(".yarnrc") },
69
+ yarnrc_yml_file: dependency_files.find { |f| f.name.end_with?(".yarnrc.yml") }
70
+ ).registry_from_rc(project_name)
71
+ end
59
72
  end
60
73
  end
61
74
  end