dependabot-npm_and_yarn 0.213.0 → 0.214.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -16,31 +16,77 @@ module Dependabot
16
16
  6
17
17
  end
18
18
 
19
- # Run any number of yarn commands while ensuring that `enableScripts` is
20
- # set to false. Yarn commands should _not_ be ran outside of this helper
21
- # to ensure that postinstall scripts are never executed, as they could
22
- # contain malicious code.
23
- def self.run_yarn_commands(*commands)
24
- # We never want to execute postinstall scripts
25
- SharedHelpers.run_shell_command("yarn config set enableScripts false")
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
26
60
  if (http_proxy = ENV.fetch("HTTP_PROXY", false))
27
61
  SharedHelpers.run_shell_command("yarn config set httpProxy #{http_proxy}")
28
62
  end
29
63
  if (https_proxy = ENV.fetch("HTTPS_PROXY", false))
30
64
  SharedHelpers.run_shell_command("yarn config set httpsProxy #{https_proxy}")
31
65
  end
32
- if (ca_file_path = ENV.fetch("NODE_EXTRA_CA_CERTS", false))
33
- output = SharedHelpers.run_shell_command("yarn --version")
34
- major_version = Version.new(output).major
35
- if major_version >= 4
36
- SharedHelpers.run_shell_command("yarn config set httpsCaFilePath #{ca_file_path}")
37
- else
38
- SharedHelpers.run_shell_command("yarn config set caFilePath #{ca_file_path}")
39
- 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}")
40
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
41
81
  commands.each { |cmd| SharedHelpers.run_shell_command(cmd) }
42
82
  end
43
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
+
44
90
  def self.dependencies_with_all_versions_metadata(dependency_set)
45
91
  working_set = Dependabot::NpmAndYarn::FileParser::DependencySet.new
46
92
  dependencies = []
@@ -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
 
@@ -12,6 +12,7 @@ module Dependabot
12
12
  https://registry.npmjs.org
13
13
  http://registry.npmjs.org
14
14
  https://registry.yarnpkg.com
15
+ http://registry.yarnpkg.com
15
16
  ).freeze
16
17
  NPM_AUTH_TOKEN_REGEX = %r{//(?<registry>.*)/:_authToken=(?<token>.*)$}
17
18
  NPM_GLOBAL_REGISTRY_REGEX = /^registry\s*=\s*['"]?(?<registry>.*?)['"]?$/
@@ -145,10 +146,13 @@ module Dependabot
145
146
  npmrc_file.content.scan(NPM_AUTH_TOKEN_REGEX) do
146
147
  next if Regexp.last_match[:registry].include?("${")
147
148
 
149
+ registry = Regexp.last_match[:registry]
150
+ token = Regexp.last_match[:token]&.strip
151
+
148
152
  registries << {
149
153
  "type" => "npm_registry",
150
- "registry" => Regexp.last_match[:registry],
151
- "token" => Regexp.last_match[:token]&.strip
154
+ "registry" => registry.gsub(/\s+/, "%20"),
155
+ "token" => token
152
156
  }
153
157
  end
154
158
 
@@ -157,7 +161,8 @@ module Dependabot
157
161
 
158
162
  registry = Regexp.last_match[:registry].strip.
159
163
  sub(%r{/+$}, "").
160
- sub(%r{^.*?//}, "")
164
+ sub(%r{^.*?//}, "").
165
+ gsub(/\s+/, "%20")
161
166
  next if registries.map { |r| r["registry"] }.include?(registry)
162
167
 
163
168
  registries << {
@@ -179,7 +184,8 @@ module Dependabot
179
184
 
180
185
  registry = Regexp.last_match[:registry].strip.
181
186
  sub(%r{/+$}, "").
182
- sub(%r{^.*?//}, "")
187
+ sub(%r{^.*?//}, "").
188
+ gsub(/\s+/, "%20")
183
189
  registries << {
184
190
  "type" => "npm_registry",
185
191
  "registry" => registry,
@@ -19,19 +19,21 @@ module Dependabot
19
19
  class UpdateChecker
20
20
  class SubdependencyVersionResolver
21
21
  def initialize(dependency:, credentials:, dependency_files:,
22
- ignored_versions:, latest_allowable_version:)
22
+ ignored_versions:, latest_allowable_version:, repo_contents_path:)
23
23
  @dependency = dependency
24
24
  @credentials = credentials
25
25
  @dependency_files = dependency_files
26
26
  @ignored_versions = ignored_versions
27
27
  @latest_allowable_version = latest_allowable_version
28
+ @repo_contents_path = repo_contents_path
28
29
  end
29
30
 
30
31
  def latest_resolvable_version
31
32
  raise "Not a subdependency!" if dependency.requirements.any?
32
33
  return if bundled_dependency?
33
34
 
34
- SharedHelpers.in_a_temporary_directory do
35
+ base_dir = dependency_files.first.directory
36
+ SharedHelpers.in_a_temporary_repo_directory(base_dir, repo_contents_path) do
35
37
  dependency_files_builder.write_temporary_dependency_files
36
38
 
37
39
  updated_lockfiles = filtered_lockfiles.map do |lockfile|
@@ -53,13 +55,13 @@ module Dependabot
53
55
  private
54
56
 
55
57
  attr_reader :dependency, :credentials, :dependency_files,
56
- :ignored_versions, :latest_allowable_version
58
+ :ignored_versions, :latest_allowable_version, :repo_contents_path
57
59
 
58
60
  def update_subdependency_in_lockfile(lockfile)
59
61
  lockfile_name = Pathname.new(lockfile.name).basename.to_s
60
62
  path = Pathname.new(lockfile.name).dirname.to_s
61
63
 
62
- updated_files = if lockfile.name.end_with?("yarn.lock") && yarn_berry?(lockfile)
64
+ updated_files = if lockfile.name.end_with?("yarn.lock") && Helpers.yarn_berry?(lockfile)
63
65
  run_yarn_berry_updater(path, lockfile_name)
64
66
  elsif lockfile.name.end_with?("yarn.lock")
65
67
  run_yarn_updater(path, lockfile_name)
@@ -70,15 +72,6 @@ module Dependabot
70
72
  updated_files.fetch(lockfile_name)
71
73
  end
72
74
 
73
- def yarn_berry?(yarn_lock)
74
- return false unless Experiments.enabled?(:yarn_berry)
75
-
76
- yaml = YAML.safe_load(yarn_lock.content)
77
- yaml.key?("__metadata")
78
- rescue StandardError
79
- false
80
- end
81
-
82
75
  def version_from_updated_lockfiles(updated_lockfiles)
83
76
  updated_files = dependency_files -
84
77
  dependency_files_builder.yarn_locks -
@@ -124,7 +117,7 @@ module Dependabot
124
117
  SharedHelpers.with_git_configured(credentials: credentials) do
125
118
  Dir.chdir(path) do
126
119
  Helpers.run_yarn_commands(
127
- "yarn up -R #{dependency.name}"
120
+ "yarn up -R #{dependency.name} #{Helpers.yarn_berry_args}".strip
128
121
  )
129
122
  { lockfile_name => File.read(lockfile_name) }
130
123
  end
@@ -38,6 +38,15 @@ module Dependabot
38
38
  "(?<required_dep>[^"]+)"
39
39
  /x
40
40
 
41
+ # Error message from yarn add:
42
+ # YN0060: │ eve-roster@workspace:. provides jest (p8d618) \
43
+ # with version 29.3.0, which doesn't satisfy \
44
+ # what ts-jest requests\n
45
+ YARN_BERRY_PEER_DEP_ERROR_REGEX =
46
+ /
47
+ YN0060:\s|\s.+\sprovides\s(?<required_dep>.+?)\s\((?<info_hash>\w+)\).+what\s(?<requiring_dep>.+?)\srequests
48
+ /x
49
+
41
50
  # Error message from npm install:
42
51
  # react-dom@15.2.0 requires a peer of react@^15.2.0 \
43
52
  # but none is installed. You must install peer dependencies yourself.
@@ -62,7 +71,7 @@ module Dependabot
62
71
  /x
63
72
 
64
73
  def initialize(dependency:, credentials:, dependency_files:,
65
- latest_allowable_version:, latest_version_finder:)
74
+ latest_allowable_version:, latest_version_finder:, repo_contents_path:)
66
75
  @dependency = dependency
67
76
  @credentials = credentials
68
77
  @dependency_files = dependency_files
@@ -70,6 +79,7 @@ module Dependabot
70
79
 
71
80
  @latest_version_finder = {}
72
81
  @latest_version_finder[dependency] = latest_version_finder
82
+ @repo_contents_path = repo_contents_path
73
83
  end
74
84
 
75
85
  def latest_resolvable_version
@@ -135,7 +145,7 @@ module Dependabot
135
145
  private
136
146
 
137
147
  attr_reader :dependency, :credentials, :dependency_files,
138
- :latest_allowable_version
148
+ :latest_allowable_version, :repo_contents_path
139
149
 
140
150
  def latest_version_finder(dep)
141
151
  @latest_version_finder[dep] ||=
@@ -307,30 +317,15 @@ module Dependabot
307
317
  # TODO: Add all of the error handling that the FileUpdater does
308
318
  # here (since problematic repos will be resolved here before they're
309
319
  # seen by the FileUpdater)
310
- SharedHelpers.in_a_temporary_directory do
320
+ base_dir = dependency_files.first.directory
321
+ SharedHelpers.in_a_temporary_repo_directory(base_dir, repo_contents_path) do
311
322
  dependency_files_builder.write_temporary_dependency_files
312
323
 
313
324
  filtered_package_files.flat_map do |file|
314
325
  path = Pathname.new(file.name).dirname
315
326
  run_checker(path: path, version: version)
316
327
  rescue SharedHelpers::HelperSubprocessFailed => e
317
- errors = []
318
- if e.message.match?(NPM6_PEER_DEP_ERROR_REGEX)
319
- e.message.scan(NPM6_PEER_DEP_ERROR_REGEX) do
320
- errors << Regexp.last_match.named_captures
321
- end
322
- elsif e.message.match?(NPM8_PEER_DEP_ERROR_REGEX)
323
- e.message.scan(NPM8_PEER_DEP_ERROR_REGEX) do
324
- errors << Regexp.last_match.named_captures
325
- end
326
- elsif e.message.match?(YARN_PEER_DEP_ERROR_REGEX)
327
- e.message.scan(YARN_PEER_DEP_ERROR_REGEX) do
328
- errors << Regexp.last_match.named_captures
329
- end
330
- else
331
- raise
332
- end
333
- errors
328
+ handle_peer_dependency_errors(e)
334
329
  end.compact
335
330
  end
336
331
  rescue SharedHelpers::HelperSubprocessFailed
@@ -340,6 +335,30 @@ module Dependabot
340
335
  []
341
336
  end
342
337
 
338
+ def handle_peer_dependency_errors(error)
339
+ errors = []
340
+ if error.message.match?(NPM6_PEER_DEP_ERROR_REGEX)
341
+ error.message.scan(NPM6_PEER_DEP_ERROR_REGEX) do
342
+ errors << Regexp.last_match.named_captures
343
+ end
344
+ elsif error.message.match?(NPM8_PEER_DEP_ERROR_REGEX)
345
+ error.message.scan(NPM8_PEER_DEP_ERROR_REGEX) do
346
+ errors << Regexp.last_match.named_captures
347
+ end
348
+ elsif error.message.match?(YARN_PEER_DEP_ERROR_REGEX)
349
+ error.message.scan(YARN_PEER_DEP_ERROR_REGEX) do
350
+ errors << Regexp.last_match.named_captures
351
+ end
352
+ elsif error.message.match?(YARN_BERRY_PEER_DEP_ERROR_REGEX)
353
+ error.message.scan(YARN_BERRY_PEER_DEP_ERROR_REGEX) do
354
+ errors << Regexp.last_match.named_captures
355
+ end
356
+ else
357
+ raise
358
+ end
359
+ errors
360
+ end
361
+
343
362
  def unmet_peer_dependencies
344
363
  peer_dependency_errors.
345
364
  map { |captures| error_details_from_captures(captures) }
@@ -351,13 +370,14 @@ module Dependabot
351
370
  end
352
371
 
353
372
  def error_details_from_captures(captures)
373
+ required_dep_captures = captures.fetch("required_dep")
374
+ requiring_dep_captures = captures.fetch("requiring_dep")
375
+ return {} unless required_dep_captures && requiring_dep_captures
376
+
354
377
  {
355
- requirement_name:
356
- captures.fetch("required_dep").sub(/@[^@]+$/, ""),
357
- requirement_version:
358
- captures.fetch("required_dep").split("@").last.delete('"'),
359
- requiring_dep_name:
360
- captures.fetch("requiring_dep").sub(/@[^@]+$/, "")
378
+ requirement_name: required_dep_captures.sub(/@[^@]+$/, ""),
379
+ requirement_version: required_dep_captures.split("@").last.delete('"'),
380
+ requiring_dep_name: requiring_dep_captures.sub(/@[^@]+$/, "")
361
381
  }
362
382
  end
363
383
 
@@ -468,13 +488,37 @@ module Dependabot
468
488
  def run_checker(path:, version:)
469
489
  # If there are both yarn lockfiles and npm lockfiles only run the
470
490
  # yarn updater
471
- if lockfiles_for_path(lockfiles: dependency_files_builder.yarn_locks, path: path).any?
491
+ lockfiles = lockfiles_for_path(lockfiles: dependency_files_builder.yarn_locks, path: path)
492
+ if lockfiles.any?
493
+ return run_yarn_berry_checker(path: path, version: version) if Helpers.yarn_berry?(lockfiles.first)
494
+
472
495
  return run_yarn_checker(path: path, version: version)
473
496
  end
474
497
 
475
498
  run_npm_checker(path: path, version: version)
476
499
  end
477
500
 
501
+ def run_yarn_berry_checker(path:, version:)
502
+ # This method mimics calling a native helper in order to comply with the caller's expectations
503
+ # Specifically we add the dependency at the specified updated version
504
+ # then check the output of the add command for Peer Dependency errors (Denoted by YN0060)
505
+ # If we find peer dependency issues, we raise HelperSubprocessFailed as
506
+ # the native helpers do.
507
+ SharedHelpers.with_git_configured(credentials: credentials) do
508
+ Dir.chdir(path) do
509
+ output = Helpers.run_yarn_command(
510
+ "yarn add #{dependency.name}@#{version} #{Helpers.yarn_berry_args}".strip
511
+ )
512
+ if output.include?("YN0060")
513
+ raise SharedHelpers::HelperSubprocessFailed.new(
514
+ message: output,
515
+ error_context: {}
516
+ )
517
+ end
518
+ end
519
+ end
520
+ end
521
+
478
522
  def run_yarn_checker(path:, version:)
479
523
  SharedHelpers.with_git_configured(credentials: credentials) do
480
524
  Dir.chdir(path) do
@@ -22,7 +22,7 @@ module Dependabot
22
22
  dependency.version &&
23
23
  version_class.correct?(dependency.version) &&
24
24
  vulnerable_versions.any? &&
25
- !vulnerable_versions.include?(version_class.new(dependency.version))
25
+ !vulnerable_versions.include?(current_version)
26
26
 
27
27
  super
28
28
  end
@@ -339,7 +339,8 @@ module Dependabot
339
339
  credentials: credentials,
340
340
  dependency_files: dependency_files,
341
341
  latest_allowable_version: latest_version,
342
- latest_version_finder: latest_version_finder
342
+ latest_version_finder: latest_version_finder,
343
+ repo_contents_path: repo_contents_path
343
344
  )
344
345
  end
345
346
 
@@ -350,7 +351,8 @@ module Dependabot
350
351
  credentials: credentials,
351
352
  dependency_files: dependency_files,
352
353
  ignored_versions: ignored_versions,
353
- latest_allowable_version: latest_version
354
+ latest_allowable_version: latest_version,
355
+ repo_contents_path: repo_contents_path
354
356
  )
355
357
  end
356
358
 
@@ -24,3 +24,5 @@ Dependabot::Dependency.register_production_check(
24
24
  groups.include?("dependencies")
25
25
  end
26
26
  )
27
+
28
+ Dependabot::Utils.register_always_clone("npm_and_yarn")
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dependabot-npm_and_yarn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.213.0
4
+ version: 0.214.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-10-31 00:00:00.000000000 Z
11
+ date: 2022-12-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dependabot-common
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 0.213.0
19
+ version: 0.214.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 0.213.0
26
+ version: 0.214.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: debug
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 3.13.0
61
+ version: 4.0.0
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 3.13.0
68
+ version: 4.0.0
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rake
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -114,14 +114,14 @@ dependencies:
114
114
  requirements:
115
115
  - - "~>"
116
116
  - !ruby/object:Gem::Version
117
- version: 1.37.1
117
+ version: 1.39.0
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
- version: 1.37.1
124
+ version: 1.39.0
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: rubocop-performance
127
127
  requirement: !ruby/object:Gem::Requirement
@@ -272,7 +272,6 @@ files:
272
272
  - lib/dependabot/npm_and_yarn/file_fetcher/path_dependency_builder.rb
273
273
  - lib/dependabot/npm_and_yarn/file_parser.rb
274
274
  - lib/dependabot/npm_and_yarn/file_parser/lockfile_parser.rb
275
- - lib/dependabot/npm_and_yarn/file_parser/yarn_lockfile_parser.rb
276
275
  - lib/dependabot/npm_and_yarn/file_updater.rb
277
276
  - lib/dependabot/npm_and_yarn/file_updater/npm_lockfile_updater.rb
278
277
  - lib/dependabot/npm_and_yarn/file_updater/npmrc_builder.rb
@@ -1,59 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "dependabot/dependency_file"
4
- require "dependabot/npm_and_yarn/file_parser"
5
-
6
- module Dependabot
7
- module NpmAndYarn
8
- class FileParser
9
- class YarnLockfileParser
10
- def initialize(lockfile:)
11
- @content = lockfile.content
12
- end
13
-
14
- # This is *extremely* crude, but saves us from having to shell out
15
- # to Yarn, which may not be safe
16
- def parse
17
- yaml = convert_to_yaml
18
- lockfile_object = parse_as_yaml(yaml)
19
- expand_lockfile_requirements(lockfile_object)
20
- end
21
-
22
- private
23
-
24
- attr_reader :content
25
-
26
- # Transform lockfile to parseable YAML by wrapping requirements in
27
- # quotes, e.g. ("pkg@1.0.0":) and adding colon to nested
28
- # properties (version: "1.0.0")
29
- def convert_to_yaml
30
- sanitize_requirement = lambda do |line|
31
- return line unless line.match?(/^[\w"]/)
32
-
33
- "\"#{line.gsub(/\"|:\n$/, '')}\":\n"
34
- end
35
- add_missing_colon = ->(l) { l.sub(/(?<=\w|")\s(?=\w|")/, ": ") }
36
-
37
- content.lines.map(&sanitize_requirement).map(&add_missing_colon).join
38
- end
39
-
40
- def parse_as_yaml(yaml)
41
- YAML.safe_load(yaml)
42
- rescue Psych::SyntaxError, Psych::DisallowedClass, Psych::BadAlias
43
- {}
44
- end
45
-
46
- # Split all comma separated keys and duplicate the lockfile entry
47
- # so we get one entry per version requirement, this is needed when
48
- # one of the requirements specifies a file: requirement, e.g.
49
- # "pkga@file:./pkg, pkgb@1.0.0 and we want to check this in
50
- # `details_from_yarn_lock`
51
- def expand_lockfile_requirements(lockfile_object)
52
- lockfile_object.to_a.each_with_object({}) do |(names, val), res|
53
- names.split(", ").each { |name| res[name] = val }
54
- end
55
- end
56
- end
57
- end
58
- end
59
- end