dependabot-npm_and_yarn 0.385.0 → 0.386.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4e68f24f2520040b0fb94bb7a0d98af8b36a23bc7a8d27aecf9af8d32830a69d
4
- data.tar.gz: 5e81bc9fc5e886d2e1a392899d27443f6d32cafdd08a788eb71052b7abe3266f
3
+ metadata.gz: eed1a15784e78d8843afd8b8e043e63bf1c3f9af24945e5980244dbdbc20012b
4
+ data.tar.gz: 44a369d54a689ae0ec9388bee4a79fedb992ce301f20037cbfd8668f3b430d2b
5
5
  SHA512:
6
- metadata.gz: 618893dff8d6152043cfeab8c89c716f1cf5998f62a6210236df62c726d7c47e4edca330446c10a93c0e4f0704319c3d1ff6be5ddb1a652f5d67e2f51e3e11f5
7
- data.tar.gz: 5a82b1c15fb5eb39f269f1d14a072796157758b9d9272f09fbcc1462296d10556d598dfb44b5f379097466661dcc0724f6d2d016dc850b8b2ab0727a8a611ff9
6
+ metadata.gz: 734cdc4cb55dd3b1361fc128f9590b891ccd6f6d45e67872f71ff9cb5ab5826eb4c9ec4814f09e4e4e046b642b2756366f06fdfd1dba5f3c5e2d8ade6617558a
7
+ data.tar.gz: 2449823cbdc0f90c27463aa6c04729a5427728ad9747404968ef7007d295f061b7436ed0bff9307dee43897c0c87d1b21d1e8a6426a43193755d54ccfa7348b1
@@ -123,7 +123,7 @@ export async function findVulnerableDependencies(
123
123
 
124
124
  const { server, url: auditRegistryUrl } = await startAuditServer(advisories);
125
125
 
126
- const arb = new Arborist({
126
+ const arboristOptions = {
127
127
  path: directory,
128
128
  auditRegistry: auditRegistryUrl,
129
129
  noProxy: "127.0.0.1",
@@ -133,7 +133,8 @@ export async function findVulnerableDependencies(
133
133
  ignoreScripts: true,
134
134
  ...registryOpts,
135
135
  ...registryCreds,
136
- });
136
+ };
137
+ const arb = new Arborist(arboristOptions);
137
138
 
138
139
  try {
139
140
  const name = advisories[0].dependency_name;
@@ -218,7 +219,12 @@ export async function findVulnerableDependencies(
218
219
 
219
220
  response.top_level_ancestors = [...topLevelAncestors].sort();
220
221
 
221
- const fixTree = await arb.audit({
222
+ // Compute the fix tree with a fresh Arborist instance: walking the audit
223
+ // report above (Vuln#isVulnerable in buildDependencyChains) mutates
224
+ // vuln.nodes, and a polluted node set corrupts the fix calculation for
225
+ // workspace repos, leaving the vulnerable version in place.
226
+ const fixArb = new Arborist(arboristOptions);
227
+ const fixTree = await fixArb.audit({
222
228
  fix: true,
223
229
  });
224
230
 
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "test-workspace-transitive-dependency",
3
+ "version": "1.0.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "test-workspace-transitive-dependency",
9
+ "version": "1.0.0",
10
+ "workspaces": [
11
+ "packages/*"
12
+ ]
13
+ },
14
+ "node_modules/@dependabot-fixtures/npm-intermediate-dependency": {
15
+ "version": "0.0.2",
16
+ "resolved": "https://registry.npmjs.org/@dependabot-fixtures/npm-intermediate-dependency/-/npm-intermediate-dependency-0.0.2.tgz",
17
+ "integrity": "sha512-xj/Xn9vf3zO9b4XQXAWysDyMARCajkyceOqcdIdg9blkz6JPzB5e3ShGH+rAo0TBy+DN7WrFBMt1xxwdqezRLg==",
18
+ "license": "ISC",
19
+ "dependencies": {
20
+ "@dependabot-fixtures/npm-transitive-dependency": "^1.0.0"
21
+ }
22
+ },
23
+ "node_modules/@dependabot-fixtures/npm-parent-dependency": {
24
+ "version": "2.0.1",
25
+ "resolved": "https://registry.npmjs.org/@dependabot-fixtures/npm-parent-dependency/-/npm-parent-dependency-2.0.1.tgz",
26
+ "integrity": "sha512-iKNiUvswqiwhJLmP6yJv45anOm43uEuKm90LK70bhwdMMbTg2XzK7TqPv5UzmHQHCxUE8zTy82piqDf/Uqui3Q==",
27
+ "license": "ISC",
28
+ "dependencies": {
29
+ "@dependabot-fixtures/npm-intermediate-dependency": "^0.0.1"
30
+ }
31
+ },
32
+ "node_modules/@dependabot-fixtures/npm-transitive-dependency": {
33
+ "version": "1.0.0",
34
+ "resolved": "https://registry.npmjs.org/@dependabot-fixtures/npm-transitive-dependency/-/npm-transitive-dependency-1.0.0.tgz",
35
+ "integrity": "sha512-nFbzQH0TRgdzSA2/FH6MPnxZDpD+5Bgz00aD5Edgbc1wY/k8VC9s7lnk22dBTgJLwoY7MgbrnAf9rAvN08hHVg==",
36
+ "license": "ISC"
37
+ },
38
+ "node_modules/app": {
39
+ "resolved": "packages/app",
40
+ "link": true
41
+ },
42
+ "node_modules/lib": {
43
+ "resolved": "packages/lib",
44
+ "link": true
45
+ },
46
+ "packages/app": {
47
+ "name": "app",
48
+ "version": "1.0.0",
49
+ "dependencies": {
50
+ "lib": "*",
51
+ "@dependabot-fixtures/npm-parent-dependency": "^2.0.1"
52
+ }
53
+ },
54
+ "packages/lib": {
55
+ "name": "lib",
56
+ "version": "1.0.0",
57
+ "dependencies": {
58
+ "@dependabot-fixtures/npm-parent-dependency": "^2.0.1"
59
+ }
60
+ }
61
+ }
62
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "test-workspace-transitive-dependency",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "workspaces": [
6
+ "packages/*"
7
+ ]
8
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "app",
3
+ "version": "1.0.0",
4
+ "dependencies": {
5
+ "lib": "*",
6
+ "@dependabot-fixtures/npm-parent-dependency": "^2.0.1"
7
+ }
8
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "lib",
3
+ "version": "1.0.0",
4
+ "dependencies": {
5
+ "@dependabot-fixtures/npm-parent-dependency": "^2.0.1"
6
+ }
7
+ }
@@ -120,6 +120,46 @@ describe("findVulnerableDependencies", () => {
120
120
  ]);
121
121
  });
122
122
 
123
+ it("fixes a deep transitive dependency reachable only via workspace packages", async () => {
124
+ helpers.copyDependenciesTree(
125
+ "vulnerability-auditor/workspace-transitive-dependency",
126
+ tempDir
127
+ );
128
+
129
+ const advisories = [
130
+ {
131
+ dependency_name: "@dependabot-fixtures/npm-transitive-dependency",
132
+ affected_versions: ["< 1.0.1"],
133
+ },
134
+ ];
135
+ const actual = await findVulnerableDependencies(tempDir, advisories);
136
+
137
+ // The vulnerable dependency sits three levels below the workspace packages
138
+ // (app/lib -> npm-parent-dependency -> npm-intermediate-dependency ->
139
+ // npm-transitive-dependency) and isn't required by the workspace root at
140
+ // all — mirroring the Turborepo layout from issue #14984. The fix is a
141
+ // lockfile-only in-range bump of the dependency itself, so the audit must
142
+ // report it as fixable with a non-vulnerable target version even though
143
+ // all top-level ancestors are workspace packages.
144
+ expect(actual.dependency_name).toBe(
145
+ "@dependabot-fixtures/npm-transitive-dependency"
146
+ );
147
+ expect(actual.current_version).toBe("1.0.0");
148
+ expect(actual.fix_available).toBe(true);
149
+ expect(actual.target_version).toBe("1.0.1");
150
+
151
+ expect(actual.top_level_ancestors).toEqual(["app", "lib"]);
152
+
153
+ expect(actual.fix_updates).toEqual([
154
+ {
155
+ dependency_name: "@dependabot-fixtures/npm-transitive-dependency",
156
+ current_version: "1.0.0",
157
+ target_version: "1.0.1",
158
+ top_level_ancestors: ["app", "lib"],
159
+ },
160
+ ]);
161
+ });
162
+
123
163
  it("returns empty fix updates when package-lock.json is outdated", async () => {
124
164
  helpers.copyDependencies(
125
165
  "vulnerability-auditor/outdated-package-lock",
@@ -68,6 +68,7 @@ module Dependabot
68
68
 
69
69
  # corepack supported package managers
70
70
  SUPPORTED_COREPACK_PACKAGE_MANAGERS = %w(npm yarn pnpm).freeze
71
+ COREPACK_SIGNATURE_METADATA_ERROR = "No compatible signature found in package metadata"
71
72
 
72
73
  sig { params(lockfile: T.nilable(DependencyFile)).returns(Integer) }
73
74
  def self.npm_version_numeric(lockfile)
@@ -564,28 +565,33 @@ module Dependabot
564
565
  output_observer: nil,
565
566
  env: nil
566
567
  )
567
- full_command = "corepack #{name} #{command}"
568
- fingerprint = "corepack #{name} #{fingerprint || command}"
568
+ full_command = T.let("corepack #{name} #{command}", String)
569
+ command_fingerprint = T.let("corepack #{name} #{fingerprint || command}", String)
569
570
 
570
- if output_observer
571
- return Dependabot::SharedHelpers.run_shell_command(
572
- full_command,
573
- fingerprint: fingerprint,
574
- output_observer: output_observer,
575
- env: env
576
- ).strip
577
- else
578
- Dependabot::SharedHelpers.run_shell_command(
579
- full_command,
580
- fingerprint: fingerprint,
581
- env: env
582
- )
583
- end.strip
571
+ run_corepack_shell_command(
572
+ full_command,
573
+ fingerprint: command_fingerprint,
574
+ output_observer: output_observer,
575
+ env: env
576
+ )
584
577
  rescue StandardError => e
585
578
  Dependabot.logger.error("Error running package manager command: #{full_command}, Error: #{e.message}")
586
- if e.message.match?(/Response Code.*:.*404.*\(Not Found\)/) &&
587
- e.message.include?("The remote server failed to provide the requested resource")
588
- raise RegistryError.new(404, "The remote server failed to provide the requested resource")
579
+
580
+ raise_registry_error_if_not_found(e)
581
+
582
+ if retry_without_signature_verification?(error: e, env: env)
583
+ retry_env = T.must(env).merge(RegistryHelper::COREPACK_INTEGRITY_KEYS_ENV => "")
584
+ Dependabot.logger.warn(
585
+ "Corepack signature verification failed against the configured private registry. " \
586
+ "Retrying once with COREPACK_INTEGRITY_KEYS disabled for this command only."
587
+ )
588
+
589
+ return run_corepack_shell_command(
590
+ T.must(full_command),
591
+ fingerprint: T.must(command_fingerprint),
592
+ output_observer: output_observer,
593
+ env: retry_env
594
+ )
589
595
  end
590
596
 
591
597
  raise
@@ -620,6 +626,57 @@ module Dependabot
620
626
  registry_helper.find_corepack_env_variables
621
627
  end
622
628
 
629
+ sig do
630
+ params(
631
+ full_command: String,
632
+ fingerprint: String,
633
+ output_observer: CommandHelpers::OutputObserver,
634
+ env: T.nilable(T::Hash[String, String])
635
+ ).returns(String)
636
+ end
637
+ def self.run_corepack_shell_command(full_command, fingerprint:, output_observer:, env: nil)
638
+ if output_observer
639
+ return Dependabot::SharedHelpers.run_shell_command(
640
+ full_command,
641
+ fingerprint: fingerprint,
642
+ output_observer: output_observer,
643
+ env: env
644
+ ).strip
645
+ end
646
+
647
+ Dependabot::SharedHelpers.run_shell_command(
648
+ full_command,
649
+ fingerprint: fingerprint,
650
+ env: env
651
+ ).strip
652
+ end
653
+
654
+ sig { params(error: StandardError).void }
655
+ def self.raise_registry_error_if_not_found(error)
656
+ if error.message.match?(/Response Code.*:.*404.*\(Not Found\)/) &&
657
+ error.message.include?("The remote server failed to provide the requested resource")
658
+ raise RegistryError.new(404, "The remote server failed to provide the requested resource")
659
+ end
660
+ end
661
+
662
+ sig { params(error: StandardError, env: T.nilable(T::Hash[String, String])).returns(T::Boolean) }
663
+ def self.retry_without_signature_verification?(error:, env:)
664
+ return false unless env
665
+
666
+ registry = env[RegistryHelper::COREPACK_NPM_REGISTRY_ENV]
667
+ return false unless registry
668
+ return false if default_npm_registry?(registry)
669
+ return false unless error.message.include?(COREPACK_SIGNATURE_METADATA_ERROR)
670
+
671
+ !env.key?(RegistryHelper::COREPACK_INTEGRITY_KEYS_ENV)
672
+ end
673
+
674
+ sig { params(registry: String).returns(T::Boolean) }
675
+ def self.default_npm_registry?(registry)
676
+ normalized_registry = RegistryHelper.normalize_registry_url(registry)
677
+ normalized_registry == RegistryHelper::DEFAULT_NPM_REGISTRY
678
+ end
679
+
623
680
  private_class_method :run_single_yarn_command
624
681
 
625
682
  sig { params(pnpm_lock: DependencyFile).returns(T.nilable(String)) }
@@ -175,27 +175,37 @@ module Dependabot
175
175
 
176
176
  sig { returns(T.nilable(Dependabot::Source)) }
177
177
  def find_source_from_registry
178
- # Attempt to use version_listing first, as fetching the entire listing
179
- # array can be slow (if it's large)
180
- potential_sources =
181
- [
182
- get_source(latest_version_listing["repository"]),
183
- get_source(latest_version_listing["homepage"]),
184
- get_source(latest_version_listing["bugs"])
185
- ].compact
186
-
187
- return potential_sources.first if potential_sources.any?
188
-
189
- potential_sources =
190
- all_version_listings.flat_map do |_, listing|
191
- [
192
- get_source(listing["repository"]),
193
- get_source(listing["homepage"]),
194
- get_source(listing["bugs"])
195
- ]
196
- end.compact
197
-
198
- potential_sources.first
178
+ # Attempt to use the latest version listing first, as fetching the
179
+ # entire listing array can be slow (if it's large)
180
+ latest_source = source_from_listing(latest_version_listing)
181
+ return latest_source if latest_source
182
+
183
+ seen_urls = T.let(Set.new, T::Set[String])
184
+ all_version_listings.each do |_, listing|
185
+ listing_source = source_from_listing(listing, seen_urls)
186
+ return listing_source if listing_source
187
+ end
188
+
189
+ nil
190
+ end
191
+
192
+ sig do
193
+ params(
194
+ listing: T::Hash[String, T.untyped],
195
+ seen_urls: T.nilable(T::Set[String])
196
+ ).returns(T.nilable(Dependabot::Source))
197
+ end
198
+ def source_from_listing(listing, seen_urls = nil)
199
+ [listing["repository"], listing["homepage"], listing["bugs"]].each do |details|
200
+ url = get_url(details)
201
+ next unless url
202
+ next if seen_urls && !seen_urls.add?(url)
203
+
204
+ source = get_source(details)
205
+ return source if source
206
+ end
207
+
208
+ nil
199
209
  end
200
210
 
201
211
  sig { returns(T.nilable(T::Hash[T.any(String, Symbol), T.untyped])) }
@@ -23,6 +23,7 @@ module Dependabot
23
23
  COREPACK_NPM_REGISTRY_ENV = "COREPACK_NPM_REGISTRY" # For Corepack
24
24
  NPM_CONFIG_REGISTRY_ENV = "npm_config_registry" # For npm
25
25
  COREPACK_NPM_TOKEN_ENV = "COREPACK_NPM_TOKEN"
26
+ COREPACK_INTEGRITY_KEYS_ENV = "COREPACK_INTEGRITY_KEYS"
26
27
 
27
28
  # Default npm registry - no need to set env vars for this
28
29
  DEFAULT_NPM_REGISTRY = "https://registry.npmjs.org"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dependabot-npm_and_yarn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.385.0
4
+ version: 0.386.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - '='
17
17
  - !ruby/object:Gem::Version
18
- version: 0.385.0
18
+ version: 0.386.0
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - '='
24
24
  - !ruby/object:Gem::Version
25
- version: 0.385.0
25
+ version: 0.386.0
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: debug
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -286,6 +286,10 @@ files:
286
286
  - helpers/test/npm/fixtures/vulnerability-auditor/workspace-link-node/package-lock.json
287
287
  - helpers/test/npm/fixtures/vulnerability-auditor/workspace-link-node/package.json
288
288
  - helpers/test/npm/fixtures/vulnerability-auditor/workspace-link-node/packages/app/package.json
289
+ - helpers/test/npm/fixtures/vulnerability-auditor/workspace-transitive-dependency/package-lock.json
290
+ - helpers/test/npm/fixtures/vulnerability-auditor/workspace-transitive-dependency/package.json
291
+ - helpers/test/npm/fixtures/vulnerability-auditor/workspace-transitive-dependency/packages/app/package.json
292
+ - helpers/test/npm/fixtures/vulnerability-auditor/workspace-transitive-dependency/packages/lib/package.json
289
293
  - helpers/test/npm/helpers.ts
290
294
  - helpers/test/npm/vulnerability-auditor.test.ts
291
295
  - helpers/test/npm6/conflicting-dependency-parser.test.ts
@@ -377,7 +381,7 @@ licenses:
377
381
  - MIT
378
382
  metadata:
379
383
  bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
380
- changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.385.0
384
+ changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.386.0
381
385
  rdoc_options: []
382
386
  require_paths:
383
387
  - lib