dependabot-npm_and_yarn 0.235.0 → 0.237.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/helpers/lib/pnpm/lockfile-parser.js +1 -0
- data/helpers/package-lock.json +144 -132
- data/helpers/package.json +4 -4
- data/lib/dependabot/npm_and_yarn/file_fetcher.rb +25 -6
- data/lib/dependabot/npm_and_yarn/file_parser/json_lock.rb +1 -1
- data/lib/dependabot/npm_and_yarn/file_parser/lockfile_parser.rb +1 -1
- data/lib/dependabot/npm_and_yarn/file_parser/pnpm_lock.rb +1 -1
- data/lib/dependabot/npm_and_yarn/file_parser/yarn_lock.rb +1 -1
- data/lib/dependabot/npm_and_yarn/file_parser.rb +49 -75
- data/lib/dependabot/npm_and_yarn/file_updater/npm_lockfile_updater.rb +15 -21
- data/lib/dependabot/npm_and_yarn/file_updater/npmrc_builder.rb +10 -2
- data/lib/dependabot/npm_and_yarn/file_updater/package_json_preparer.rb +6 -8
- data/lib/dependabot/npm_and_yarn/file_updater/package_json_updater.rb +1 -1
- data/lib/dependabot/npm_and_yarn/file_updater/pnpm_lockfile_updater.rb +14 -5
- data/lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb +1 -1
- data/lib/dependabot/npm_and_yarn/helpers.rb +14 -9
- data/lib/dependabot/npm_and_yarn/package_manager.rb +4 -4
- data/lib/dependabot/npm_and_yarn/registry_parser.rb +75 -0
- data/lib/dependabot/npm_and_yarn/update_checker/registry_finder.rb +76 -61
- data/lib/dependabot/npm_and_yarn/update_checker/vulnerability_auditor.rb +1 -10
- data/lib/dependabot/npm_and_yarn/update_checker.rb +1 -2
- data/lib/dependabot/npm_and_yarn.rb +1 -1
- metadata +22 -7
@@ -2,6 +2,7 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "json"
|
5
|
+
require "sorbet-runtime"
|
5
6
|
require "dependabot/experiments"
|
6
7
|
require "dependabot/logger"
|
7
8
|
require "dependabot/file_fetchers"
|
@@ -14,6 +15,9 @@ require "dependabot/npm_and_yarn/file_parser/lockfile_parser"
|
|
14
15
|
module Dependabot
|
15
16
|
module NpmAndYarn
|
16
17
|
class FileFetcher < Dependabot::FileFetchers::Base # rubocop:disable Metrics/ClassLength
|
18
|
+
extend T::Sig
|
19
|
+
extend T::Helpers
|
20
|
+
|
17
21
|
require_relative "file_fetcher/path_dependency_builder"
|
18
22
|
|
19
23
|
# Npm always prefixes file paths in the lockfile "version" with "file:"
|
@@ -56,7 +60,7 @@ module Dependabot
|
|
56
60
|
def ecosystem_versions
|
57
61
|
package_managers = {}
|
58
62
|
|
59
|
-
package_managers["npm"] =
|
63
|
+
package_managers["npm"] = npm_version if package_lock
|
60
64
|
package_managers["yarn"] = yarn_version if yarn_version
|
61
65
|
package_managers["pnpm"] = pnpm_version if pnpm_version
|
62
66
|
package_managers["shrinkwrap"] = 1 if shrinkwrap
|
@@ -67,8 +71,7 @@ module Dependabot
|
|
67
71
|
}
|
68
72
|
end
|
69
73
|
|
70
|
-
|
71
|
-
|
74
|
+
sig { override.returns(T::Array[DependencyFile]) }
|
72
75
|
def fetch_files
|
73
76
|
fetched_files = []
|
74
77
|
fetched_files << package_json
|
@@ -82,6 +85,12 @@ module Dependabot
|
|
82
85
|
fetched_files.uniq
|
83
86
|
end
|
84
87
|
|
88
|
+
private
|
89
|
+
|
90
|
+
def recurse_submodules_when_cloning?
|
91
|
+
true
|
92
|
+
end
|
93
|
+
|
85
94
|
def npm_files
|
86
95
|
fetched_npm_files = []
|
87
96
|
fetched_npm_files << package_lock if package_lock
|
@@ -161,10 +170,14 @@ module Dependabot
|
|
161
170
|
@inferred_npmrc = nil
|
162
171
|
end
|
163
172
|
|
173
|
+
def npm_version
|
174
|
+
Helpers.npm_version_numeric(package_lock.content)
|
175
|
+
end
|
176
|
+
|
164
177
|
def yarn_version
|
165
178
|
return @yarn_version if defined?(@yarn_version)
|
166
179
|
|
167
|
-
@yarn_version = package_manager.
|
180
|
+
@yarn_version = package_manager.requested_version("yarn") || guess_yarn_version
|
168
181
|
end
|
169
182
|
|
170
183
|
def guess_yarn_version
|
@@ -176,13 +189,19 @@ module Dependabot
|
|
176
189
|
def pnpm_version
|
177
190
|
return @pnpm_version if defined?(@pnpm_version)
|
178
191
|
|
179
|
-
|
192
|
+
version = package_manager.requested_version("pnpm") || guess_pnpm_version
|
193
|
+
|
194
|
+
if version && Version.new(version.to_s) < Version.new("7")
|
195
|
+
raise ToolVersionNotSupported.new("PNPM", version.to_s, "7.*, 8.*")
|
196
|
+
end
|
197
|
+
|
198
|
+
@pnpm_version = version
|
180
199
|
end
|
181
200
|
|
182
201
|
def guess_pnpm_version
|
183
202
|
return unless pnpm_lock
|
184
203
|
|
185
|
-
Helpers.
|
204
|
+
Helpers.pnpm_version_numeric(pnpm_lock)
|
186
205
|
end
|
187
206
|
|
188
207
|
def package_manager
|
@@ -7,7 +7,7 @@ require "dependabot/npm_and_yarn/helpers"
|
|
7
7
|
|
8
8
|
module Dependabot
|
9
9
|
module NpmAndYarn
|
10
|
-
class FileParser
|
10
|
+
class FileParser < Dependabot::FileParsers::Base
|
11
11
|
class LockfileParser
|
12
12
|
require "dependabot/npm_and_yarn/file_parser/yarn_lock"
|
13
13
|
require "dependabot/npm_and_yarn/file_parser/pnpm_lock"
|
@@ -11,6 +11,7 @@ require "dependabot/npm_and_yarn/helpers"
|
|
11
11
|
require "dependabot/npm_and_yarn/native_helpers"
|
12
12
|
require "dependabot/npm_and_yarn/version"
|
13
13
|
require "dependabot/npm_and_yarn/requirement"
|
14
|
+
require "dependabot/npm_and_yarn/registry_parser"
|
14
15
|
require "dependabot/git_metadata_fetcher"
|
15
16
|
require "dependabot/git_commit_checker"
|
16
17
|
require "dependabot/errors"
|
@@ -34,6 +35,15 @@ module Dependabot
|
|
34
35
|
)?$
|
35
36
|
}ix
|
36
37
|
|
38
|
+
def self.each_dependency(json)
|
39
|
+
DEPENDENCY_TYPES.each do |type|
|
40
|
+
deps = json[type] || {}
|
41
|
+
deps.each do |name, requirement|
|
42
|
+
yield name, requirement, type
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
37
47
|
def parse
|
38
48
|
dependency_set = DependencySet.new
|
39
49
|
dependency_set += manifest_dependencies
|
@@ -41,11 +51,16 @@ module Dependabot
|
|
41
51
|
|
42
52
|
dependencies = Helpers.dependencies_with_all_versions_metadata(dependency_set)
|
43
53
|
|
44
|
-
# TODO: Currently, Dependabot can't handle dependencies that have both
|
45
|
-
# a git source *and* a non-git source. Fix that!
|
46
54
|
dependencies.reject do |dep|
|
47
|
-
|
48
|
-
|
55
|
+
reqs = dep.requirements
|
56
|
+
|
57
|
+
# Ignore dependencies defined in support files, since we don't want PRs for those
|
58
|
+
support_reqs = reqs.select { |r| support_package_files.any? { |f| f.name == r[:file] } }
|
59
|
+
next true if support_reqs.any?
|
60
|
+
|
61
|
+
# TODO: Currently, Dependabot can't handle dependencies that have both
|
62
|
+
# a git source *and* a non-git source. Fix that!
|
63
|
+
git_reqs = reqs.select { |r| r.dig(:source, :type) == "git" }
|
49
64
|
next false if git_reqs.none?
|
50
65
|
next true if git_reqs.map { |r| r.fetch(:source) }.uniq.count > 1
|
51
66
|
|
@@ -59,22 +74,24 @@ module Dependabot
|
|
59
74
|
dependency_set = DependencySet.new
|
60
75
|
|
61
76
|
package_files.each do |file|
|
77
|
+
json = JSON.parse(file.content)
|
78
|
+
|
62
79
|
# TODO: Currently, Dependabot can't handle flat dependency files
|
63
80
|
# (and will error at the FileUpdater stage, because the
|
64
81
|
# UpdateChecker doesn't take account of flat resolution).
|
65
|
-
next if
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
82
|
+
next if json["flat"]
|
83
|
+
|
84
|
+
self.class.each_dependency(json) do |name, requirement, type|
|
85
|
+
next unless requirement.is_a?(String)
|
86
|
+
|
87
|
+
# Skip dependencies using Yarn workspace cross-references as requirements
|
88
|
+
next if requirement.start_with?("workspace:")
|
89
|
+
|
90
|
+
requirement = "*" if requirement == ""
|
91
|
+
dep = build_dependency(
|
92
|
+
file: file, type: type, name: name, requirement: requirement
|
93
|
+
)
|
94
|
+
dependency_set << dep if dep
|
78
95
|
end
|
79
96
|
end
|
80
97
|
|
@@ -253,7 +270,10 @@ module Dependabot
|
|
253
270
|
return unless resolved_url.start_with?("http")
|
254
271
|
return if resolved_url.match?(/(?<!pkg\.)github/)
|
255
272
|
|
256
|
-
|
273
|
+
RegistryParser.new(
|
274
|
+
resolved_url: resolved_url,
|
275
|
+
credentials: credentials
|
276
|
+
).registry_source_for(name)
|
257
277
|
end
|
258
278
|
|
259
279
|
def requirement_for(requirement)
|
@@ -286,69 +306,23 @@ module Dependabot
|
|
286
306
|
}
|
287
307
|
end
|
288
308
|
|
289
|
-
def
|
290
|
-
|
291
|
-
if resolved_url.include?("/~/")
|
292
|
-
# Gemfury format
|
293
|
-
resolved_url.split("/~/").first
|
294
|
-
elsif resolved_url.include?("/#{name}/-/#{name}")
|
295
|
-
# MyGet / Bintray format
|
296
|
-
resolved_url.split("/#{name}/-/#{name}").first
|
297
|
-
.gsub("dl.bintray.com//", "api.bintray.com/npm/").
|
298
|
-
# GitLab format
|
299
|
-
gsub(%r{\/projects\/\d+}, "")
|
300
|
-
elsif resolved_url.include?("/#{name}/-/#{name.split('/').last}")
|
301
|
-
# Sonatype Nexus / Artifactory JFrog format
|
302
|
-
resolved_url.split("/#{name}/-/#{name.split('/').last}").first
|
303
|
-
elsif (cred_url = url_for_relevant_cred(resolved_url)) then cred_url
|
304
|
-
else
|
305
|
-
resolved_url.split("/")[0..2].join("/")
|
306
|
-
end
|
307
|
-
|
308
|
-
{ type: "registry", url: url }
|
309
|
+
def support_package_files
|
310
|
+
@support_package_files ||= sub_package_files.select(&:support_file?)
|
309
311
|
end
|
310
312
|
|
311
|
-
def
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
.select { |cred| cred["type"] == "npm_registry" }
|
317
|
-
.sort_by { |cred| cred["registry"].length }
|
318
|
-
.find do |details|
|
319
|
-
next true if resolved_url_host == details["registry"]
|
320
|
-
|
321
|
-
uri = if details["registry"]&.include?("://")
|
322
|
-
URI(details["registry"])
|
323
|
-
else
|
324
|
-
URI("https://#{details['registry']}")
|
325
|
-
end
|
326
|
-
resolved_url_host == uri.host && resolved_url.include?(details["registry"])
|
327
|
-
end
|
328
|
-
|
329
|
-
return unless credential_matching_url
|
330
|
-
|
331
|
-
# Trim the resolved URL so that it ends at the same point as the
|
332
|
-
# credential registry
|
333
|
-
reg = credential_matching_url["registry"]
|
334
|
-
resolved_url.gsub(/#{Regexp.quote(reg)}.*/, "") + reg
|
313
|
+
def sub_package_files
|
314
|
+
@sub_package_files ||=
|
315
|
+
dependency_files.select { |f| f.name.end_with?("package.json") }
|
316
|
+
.reject { |f| f.name == "package.json" }
|
317
|
+
.reject { |f| f.name.include?("node_modules/") }
|
335
318
|
end
|
336
319
|
|
337
320
|
def package_files
|
338
321
|
@package_files ||=
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
.reject { |f| f.name == "package.json" }
|
344
|
-
.reject { |f| f.name.include?("node_modules/") }
|
345
|
-
.reject(&:support_file?)
|
346
|
-
|
347
|
-
[
|
348
|
-
dependency_files.find { |f| f.name == "package.json" },
|
349
|
-
*sub_packages
|
350
|
-
].compact
|
351
|
-
end
|
322
|
+
[
|
323
|
+
dependency_files.find { |f| f.name == "package.json" },
|
324
|
+
*sub_package_files
|
325
|
+
].compact
|
352
326
|
end
|
353
327
|
|
354
328
|
def version_class
|
@@ -13,7 +13,7 @@ require "dependabot/shared_helpers"
|
|
13
13
|
# rubocop:disable Metrics/ClassLength
|
14
14
|
module Dependabot
|
15
15
|
module NpmAndYarn
|
16
|
-
class FileUpdater
|
16
|
+
class FileUpdater < Dependabot::FileUpdaters::Base
|
17
17
|
class NpmLockfileUpdater
|
18
18
|
require_relative "npmrc_builder"
|
19
19
|
require_relative "package_json_updater"
|
@@ -562,13 +562,11 @@ module Dependabot
|
|
562
562
|
return content if git_dependencies_to_lock.empty?
|
563
563
|
|
564
564
|
json = JSON.parse(content)
|
565
|
-
NpmAndYarn::FileParser
|
566
|
-
|
567
|
-
|
568
|
-
next unless updated_version
|
565
|
+
NpmAndYarn::FileParser.each_dependency(json) do |nm, _, type|
|
566
|
+
updated_version = git_dependencies_to_lock.dig(nm, :version)
|
567
|
+
next unless updated_version
|
569
568
|
|
570
|
-
|
571
|
-
end
|
569
|
+
json[type][nm] = git_dependencies_to_lock[nm][:version]
|
572
570
|
end
|
573
571
|
|
574
572
|
indent = detect_indentation(content)
|
@@ -605,12 +603,10 @@ module Dependabot
|
|
605
603
|
def lock_deps_with_latest_reqs(content)
|
606
604
|
json = JSON.parse(content)
|
607
605
|
|
608
|
-
NpmAndYarn::FileParser
|
609
|
-
|
610
|
-
next unless requirement == "latest"
|
606
|
+
NpmAndYarn::FileParser.each_dependency(json) do |nm, requirement, type|
|
607
|
+
next unless requirement == "latest"
|
611
608
|
|
612
|
-
|
613
|
-
end
|
609
|
+
json[type][nm] = "*"
|
614
610
|
end
|
615
611
|
|
616
612
|
indent = detect_indentation(content)
|
@@ -721,17 +717,15 @@ module Dependabot
|
|
721
717
|
|
722
718
|
dependency_names_to_restore = (dependencies.map(&:name) + git_dependencies_to_lock.keys).uniq
|
723
719
|
|
724
|
-
NpmAndYarn::FileParser
|
725
|
-
|
726
|
-
next unless dependency_names_to_restore.include?(dependency_name)
|
720
|
+
NpmAndYarn::FileParser.each_dependency(parsed_package_json) do |dependency_name, original_requirement, type|
|
721
|
+
next unless dependency_names_to_restore.include?(dependency_name)
|
727
722
|
|
728
|
-
|
729
|
-
|
723
|
+
locked_requirement = parsed_updated_lockfile_content.dig("packages", "", type, dependency_name)
|
724
|
+
next unless locked_requirement
|
730
725
|
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
end
|
726
|
+
locked_req = %("#{dependency_name}": "#{locked_requirement}")
|
727
|
+
original_req = %("#{dependency_name}": "#{original_requirement}")
|
728
|
+
updated_lockfile_content = updated_lockfile_content.gsub(locked_req, original_req)
|
735
729
|
end
|
736
730
|
|
737
731
|
updated_lockfile_content
|
@@ -5,7 +5,7 @@ require "dependabot/npm_and_yarn/file_updater"
|
|
5
5
|
|
6
6
|
module Dependabot
|
7
7
|
module NpmAndYarn
|
8
|
-
class FileUpdater
|
8
|
+
class FileUpdater < Dependabot::FileUpdaters::Base
|
9
9
|
# Build a .npmrc file from the lockfile content, credentials, and any
|
10
10
|
# committed .npmrc
|
11
11
|
# We should refactor this to use UpdateChecker::RegistryFinder
|
@@ -140,7 +140,10 @@ module Dependabot
|
|
140
140
|
@dependency_urls = dependencies.map do |dependency|
|
141
141
|
UpdateChecker::RegistryFinder.new(
|
142
142
|
dependency: dependency,
|
143
|
-
credentials: credentials
|
143
|
+
credentials: credentials,
|
144
|
+
npmrc_file: npmrc_file,
|
145
|
+
yarnrc_file: yarnrc_file,
|
146
|
+
yarnrc_yml_file: yarnrc_yml_file
|
144
147
|
).dependency_url
|
145
148
|
end
|
146
149
|
return @dependency_urls
|
@@ -309,6 +312,11 @@ module Dependabot
|
|
309
312
|
.find { |f| f.name.end_with?(".yarnrc") }
|
310
313
|
end
|
311
314
|
|
315
|
+
def yarnrc_yml_file
|
316
|
+
@yarnrc_yml_file ||= dependency_files
|
317
|
+
.find { |f| f.name.end_with?(".yarnrc.yml") }
|
318
|
+
end
|
319
|
+
|
312
320
|
def yarn_lock
|
313
321
|
@yarn_lock ||= dependency_files.find { |f| f.name == "yarn.lock" }
|
314
322
|
end
|
@@ -6,7 +6,7 @@ require "dependabot/npm_and_yarn/file_parser"
|
|
6
6
|
|
7
7
|
module Dependabot
|
8
8
|
module NpmAndYarn
|
9
|
-
class FileUpdater
|
9
|
+
class FileUpdater < Dependabot::FileUpdaters::Base
|
10
10
|
class PackageJsonPreparer
|
11
11
|
def initialize(package_json_content:)
|
12
12
|
@package_json_content = package_json_content
|
@@ -72,14 +72,12 @@ module Dependabot
|
|
72
72
|
|
73
73
|
@git_ssh_requirements_to_swap = []
|
74
74
|
|
75
|
-
NpmAndYarn::FileParser
|
76
|
-
|
77
|
-
|
78
|
-
next unless req.start_with?("git+ssh:")
|
75
|
+
NpmAndYarn::FileParser.each_dependency(JSON.parse(package_json_content)) do |_, req, _t|
|
76
|
+
next unless req.is_a?(String)
|
77
|
+
next unless req.start_with?("git+ssh:")
|
79
78
|
|
80
|
-
|
81
|
-
|
82
|
-
end
|
79
|
+
req = req.split("#").first
|
80
|
+
@git_ssh_requirements_to_swap << req
|
83
81
|
end
|
84
82
|
|
85
83
|
@git_ssh_requirements_to_swap
|
@@ -5,7 +5,7 @@ require "dependabot/npm_and_yarn/file_updater"
|
|
5
5
|
|
6
6
|
module Dependabot
|
7
7
|
module NpmAndYarn
|
8
|
-
class FileUpdater
|
8
|
+
class FileUpdater < Dependabot::FileUpdaters::Base
|
9
9
|
class PackageJsonUpdater
|
10
10
|
def initialize(package_json:, dependencies:)
|
11
11
|
@package_json = package_json
|
@@ -3,11 +3,12 @@
|
|
3
3
|
|
4
4
|
require "dependabot/npm_and_yarn/helpers"
|
5
5
|
require "dependabot/npm_and_yarn/update_checker/registry_finder"
|
6
|
+
require "dependabot/npm_and_yarn/registry_parser"
|
6
7
|
require "dependabot/shared_helpers"
|
7
8
|
|
8
9
|
module Dependabot
|
9
10
|
module NpmAndYarn
|
10
|
-
class FileUpdater
|
11
|
+
class FileUpdater < Dependabot::FileUpdaters::Base
|
11
12
|
class PnpmLockfileUpdater
|
12
13
|
require_relative "npmrc_builder"
|
13
14
|
require_relative "package_json_updater"
|
@@ -35,7 +36,8 @@ module Dependabot
|
|
35
36
|
|
36
37
|
IRRESOLVABLE_PACKAGE = "ERR_PNPM_NO_MATCHING_VERSION"
|
37
38
|
INVALID_REQUIREMENT = "ERR_PNPM_SPEC_NOT_SUPPORTED_BY_ANY_RESOLVER"
|
38
|
-
|
39
|
+
UNREACHABLE_GIT = %r{ERR_PNPM_FETCH_404[ [^:print:]]+GET (?<url>https://codeload\.github\.com/[^/]+/[^/]+)/}
|
40
|
+
MISSING_PACKAGE = /ERR_PNPM_FETCH_404[ [^:print:]]+GET (?<dependency_url>.*): Not Found - 404/
|
39
41
|
|
40
42
|
def run_pnpm_update(pnpm_lock:)
|
41
43
|
SharedHelpers.in_a_temporary_repo_directory(base_dir, repo_contents_path) do
|
@@ -87,11 +89,18 @@ module Dependabot
|
|
87
89
|
raise_resolvability_error(error_message, pnpm_lock)
|
88
90
|
end
|
89
91
|
|
92
|
+
if error_message.match?(UNREACHABLE_GIT)
|
93
|
+
dependency_url = error_message.match(UNREACHABLE_GIT).named_captures.fetch("url")
|
94
|
+
|
95
|
+
raise Dependabot::GitDependenciesNotReachable, dependency_url
|
96
|
+
end
|
97
|
+
|
90
98
|
raise unless error_message.match?(MISSING_PACKAGE)
|
91
99
|
|
92
|
-
|
93
|
-
|
94
|
-
|
100
|
+
dependency_url = error_message.match(MISSING_PACKAGE)
|
101
|
+
.named_captures["dependency_url"]
|
102
|
+
|
103
|
+
package_name = RegistryParser.new(resolved_url: dependency_url, credentials: credentials).dependency_name
|
95
104
|
raise_missing_package_error(package_name, error_message, pnpm_lock)
|
96
105
|
end
|
97
106
|
|
@@ -14,7 +14,7 @@ require "dependabot/errors"
|
|
14
14
|
# rubocop:disable Metrics/ClassLength
|
15
15
|
module Dependabot
|
16
16
|
module NpmAndYarn
|
17
|
-
class FileUpdater
|
17
|
+
class FileUpdater < Dependabot::FileUpdaters::Base
|
18
18
|
class YarnLockfileUpdater
|
19
19
|
require_relative "npmrc_builder"
|
20
20
|
require_relative "package_json_updater"
|
@@ -25,6 +25,16 @@ module Dependabot
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
+
# Mapping from lockfile versions to PNPM versions is at
|
29
|
+
# https://github.com/pnpm/spec/tree/274ff02de23376ad59773a9f25ecfedd03a41f64/lockfile, but simplify it for now.
|
30
|
+
def self.pnpm_version_numeric(pnpm_lock)
|
31
|
+
if pnpm_lockfile_version(pnpm_lock).to_f >= 5.4
|
32
|
+
8
|
33
|
+
else
|
34
|
+
6
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
28
38
|
def self.fetch_yarnrc_yml_value(key, default_value)
|
29
39
|
if File.exist?(".yarnrc.yml") && (yarnrc = YAML.load_file(".yarnrc.yml"))
|
30
40
|
yarnrc.fetch(key, default_value)
|
@@ -44,20 +54,11 @@ module Dependabot
|
|
44
54
|
@yarn_major_version ||= fetch_yarn_major_version
|
45
55
|
end
|
46
56
|
|
47
|
-
def self.pnpm_major_version
|
48
|
-
@pnpm_major_version ||= fetch_pnpm_major_version
|
49
|
-
end
|
50
|
-
|
51
57
|
def self.fetch_yarn_major_version
|
52
58
|
output = SharedHelpers.run_shell_command("yarn --version")
|
53
59
|
Version.new(output).major
|
54
60
|
end
|
55
61
|
|
56
|
-
def self.fetch_pnpm_major_version
|
57
|
-
output = SharedHelpers.run_shell_command("pnpm --version")
|
58
|
-
Version.new(output).major
|
59
|
-
end
|
60
|
-
|
61
62
|
def self.yarn_zero_install?
|
62
63
|
File.exist?(".pnp.cjs")
|
63
64
|
end
|
@@ -122,6 +123,10 @@ module Dependabot
|
|
122
123
|
SharedHelpers.run_shell_command(command, fingerprint: fingerprint)
|
123
124
|
end
|
124
125
|
|
126
|
+
def self.pnpm_lockfile_version(pnpm_lock)
|
127
|
+
pnpm_lock.content.match(/^lockfileVersion: ['"]?(?<version>[\d.]+)/)[:version]
|
128
|
+
end
|
129
|
+
|
125
130
|
def self.dependencies_with_all_versions_metadata(dependency_set)
|
126
131
|
dependency_set.dependencies.map do |dependency|
|
127
132
|
dependency.metadata[:all_versions] = dependency_set.all_versions_for_name(dependency.name)
|
@@ -8,11 +8,11 @@ module Dependabot
|
|
8
8
|
@package_json = package_json
|
9
9
|
end
|
10
10
|
|
11
|
-
def
|
12
|
-
|
13
|
-
return unless
|
11
|
+
def requested_version(name)
|
12
|
+
version = @package_json.fetch("packageManager", nil)
|
13
|
+
return unless version
|
14
14
|
|
15
|
-
version_match =
|
15
|
+
version_match = version.match(/#{name}@(?<version>\d+.\d+.\d+)/)
|
16
16
|
version_match&.named_captures&.fetch("version", nil)
|
17
17
|
end
|
18
18
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Dependabot
|
5
|
+
module NpmAndYarn
|
6
|
+
class RegistryParser
|
7
|
+
def initialize(resolved_url:, credentials:)
|
8
|
+
@resolved_url = resolved_url
|
9
|
+
@credentials = credentials
|
10
|
+
end
|
11
|
+
|
12
|
+
def registry_source_for(name)
|
13
|
+
url =
|
14
|
+
if resolved_url.include?("/~/")
|
15
|
+
# Gemfury format
|
16
|
+
resolved_url.split("/~/").first
|
17
|
+
elsif resolved_url.include?("/#{name}/-/#{name}")
|
18
|
+
# MyGet / Bintray format
|
19
|
+
resolved_url.split("/#{name}/-/#{name}").first
|
20
|
+
.gsub("dl.bintray.com//", "api.bintray.com/npm/").
|
21
|
+
# GitLab format
|
22
|
+
gsub(%r{\/projects\/\d+}, "")
|
23
|
+
elsif resolved_url.include?("/#{name}/-/#{name.split('/').last}")
|
24
|
+
# Sonatype Nexus / Artifactory JFrog format
|
25
|
+
resolved_url.split("/#{name}/-/#{name.split('/').last}").first
|
26
|
+
elsif (cred_url = url_for_relevant_cred) then cred_url
|
27
|
+
else
|
28
|
+
resolved_url.split("/")[0..2].join("/")
|
29
|
+
end
|
30
|
+
|
31
|
+
{ type: "registry", url: url }
|
32
|
+
end
|
33
|
+
|
34
|
+
def dependency_name
|
35
|
+
url_base = if resolved_url.include?("/-/")
|
36
|
+
resolved_url.split("/-/").first
|
37
|
+
else
|
38
|
+
resolved_url
|
39
|
+
end
|
40
|
+
|
41
|
+
url_base.split("/")[3..-1].join("/").gsub("%2F", "/")
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
attr_reader :resolved_url, :credentials
|
47
|
+
|
48
|
+
def url_for_relevant_cred
|
49
|
+
resolved_url_host = URI(resolved_url).host
|
50
|
+
|
51
|
+
credential_matching_url =
|
52
|
+
credentials
|
53
|
+
.select { |cred| cred["type"] == "npm_registry" }
|
54
|
+
.sort_by { |cred| cred["registry"].length }
|
55
|
+
.find do |details|
|
56
|
+
next true if resolved_url_host == details["registry"]
|
57
|
+
|
58
|
+
uri = if details["registry"]&.include?("://")
|
59
|
+
URI(details["registry"])
|
60
|
+
else
|
61
|
+
URI("https://#{details['registry']}")
|
62
|
+
end
|
63
|
+
resolved_url_host == uri.host && resolved_url.include?(details["registry"])
|
64
|
+
end
|
65
|
+
|
66
|
+
return unless credential_matching_url
|
67
|
+
|
68
|
+
# Trim the resolved URL so that it ends at the same point as the
|
69
|
+
# credential registry
|
70
|
+
reg = credential_matching_url["registry"]
|
71
|
+
resolved_url.gsub(/#{Regexp.quote(reg)}.*/, "") + reg
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|