dependabot-npm_and_yarn 0.383.0 → 0.384.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: 36bb333ca707b5155b7c82a95c5a3e934a5b7acf0ae96723bf9e3ef46723679f
4
- data.tar.gz: '0889ddfed838d230dd4a7e299847816690a4151ac4cd81b6230160a933e44e67'
3
+ metadata.gz: 50054abe467b1c8831c9fe1d24a014dc0944fff9b5991fd2b19acb92f05d5253
4
+ data.tar.gz: 81d4161505d10bfbafb2a6f7bd66d79dcdf70eb03cb56e3bf187cd795b4ba4c4
5
5
  SHA512:
6
- metadata.gz: 162d5f20b24ad893b8ae76c6389ded05c31309f7fb43a4f1ce43186cfe8c0cafea09816e245b1a30e3ecc4ffc5f3792a01e5167f5aff09c9f86ff8baa1fa7d11
7
- data.tar.gz: 8a97cb90adf36509589b5309bb50036a2d6f86def852456dadec850c8fec0b3b532042898716f1c4920587d42da48b9d62dd08ad57f3d6d7b20821a9c6d11a53
6
+ metadata.gz: a7641af7236364d01c54cabb30389d3f65a7831b4fcae0484cac7777dd4c5621bdbd144d13ed520f84f5d7d9d759be2bf0770c51e251cbb0f1644ff77364d0f5
7
+ data.tar.gz: 24ada1ea83d5ca4383168a8e8f9847a3e63c06cfb3c246e1dc1e16cd53688afa1c34fadc22bb27954ff333bfc6f10fc0f25b607e54d6a4c2c3add55bdb91ed5b
@@ -80,7 +80,7 @@ module Dependabot
80
80
  end
81
81
 
82
82
  sig { override.returns(T::Array[DependencyFile]) }
83
- def fetch_files
83
+ def fetch_files # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
84
84
  fetched_files = T.let([], T::Array[DependencyFile])
85
85
  fetched_files << package_json
86
86
  fetched_files << T.must(npmrc) if npmrc && !scope_overrides_npmrc?
@@ -91,6 +91,17 @@ module Dependabot
91
91
  fetched_files += workspace_package_jsons
92
92
  fetched_files += path_dependencies(fetched_files)
93
93
 
94
+ # When no package manager version is detected at all (no lockfile, no
95
+ # packageManager, no engines) AND no committed .npmrc exists, the
96
+ # inferred_npmrc path inside npm_files is never reached. Try generating
97
+ # an .npmrc from scope credentials, or reject if no config is available.
98
+ # Skip for yarn/pnpm-only projects where npm isn't the relevant manager.
99
+ if no_package_manager_detected? && npmrc.nil?
100
+ generated = inferred_npmrc
101
+ fetched_files << generated if generated
102
+ reject_if_private_registry_without_config! unless generated
103
+ end
104
+
94
105
  # Filter excluded files from final collection
95
106
  filtered_files = fetched_files.uniq.reject do |file|
96
107
  Dependabot::Experiments.enabled?(:enable_exclude_paths_subdirectory_manifest_files) &&
@@ -156,7 +167,7 @@ module Dependabot
156
167
  if Dependabot::Experiments.enabled?(:enable_npmrc_credential_generation) && credentials_have_scope?
157
168
  npmrc_from_credentials = generate_npmrc_from_credentials
158
169
  if npmrc_from_credentials
159
- Dependabot.logger.info("Generated .npmrc from credential scope configuration (overrides committed .npmrc)")
170
+ Dependabot.logger.warn("Generated .npmrc from credential scope configuration (overrides committed .npmrc)")
160
171
  return @inferred_npmrc ||= T.let(npmrc_from_credentials, T.nilable(DependencyFile))
161
172
  end
162
173
  end
@@ -236,8 +247,10 @@ module Dependabot
236
247
  end
237
248
 
238
249
  sig { void }
239
- def reject_if_private_registry_without_config!
250
+ def reject_if_private_registry_without_config! # rubocop:disable Metrics/PerceivedComplexity
240
251
  return unless Dependabot::Experiments.enabled?(:enable_npmrc_credential_generation)
252
+ return if credentials_have_scope?
253
+ return if wrapped_credentials.any?(&:replaces_base?)
241
254
 
242
255
  private_registry_creds = wrapped_credentials.select do |cred|
243
256
  next false unless cred["type"] == "npm_registry"
@@ -257,7 +270,7 @@ module Dependabot
257
270
 
258
271
  sig { returns(T::Boolean) }
259
272
  def credentials_have_scope?
260
- wrapped_credentials.any? { |cred| cred["type"] == "npm_registry" && cred.scope }
273
+ wrapped_credentials.any? { |cred| cred["type"] == "npm_registry" && cred.scope&.any? }
261
274
  end
262
275
 
263
276
  # file_fetcher_command.rb may pass raw Hashes as credentials at runtime.
@@ -286,6 +299,11 @@ module Dependabot
286
299
  Dependabot::Experiments.enabled?(:enable_npmrc_credential_generation) && credentials_have_scope?
287
300
  end
288
301
 
302
+ sig { returns(T::Boolean) }
303
+ def no_package_manager_detected?
304
+ !npm_version && !yarn_version && !pnpm_version
305
+ end
306
+
289
307
  sig { returns(T.nilable(T.any(Integer, String))) }
290
308
  def npm_version
291
309
  @npm_version ||= T.let(package_manager_helper.setup(NpmPackageManager::NAME), T.nilable(T.any(Integer, String)))
@@ -3,6 +3,7 @@
3
3
 
4
4
  # See https://docs.npmjs.com/files/package.json for package.json format docs.
5
5
 
6
+ require "cgi/escape"
6
7
  require "dependabot/dependency"
7
8
  require "dependabot/file_parsers"
8
9
  require "dependabot/file_parsers/base"
@@ -341,7 +341,10 @@ module Dependabot
341
341
  dependency_names = sub_dependencies.map(&:name)
342
342
  original_content = File.read(lockfile_basename)
343
343
 
344
- NativeHelpers.run_npm8_subdependency_update_command(dependency_names)
344
+ NativeHelpers.run_npm8_subdependency_update_command(
345
+ dependency_names,
346
+ security_updates_only: security_updates_only?
347
+ )
345
348
 
346
349
  updated_content = File.read(lockfile_basename)
347
350
  if updated_content == original_content && Dependabot::Experiments.enabled?(:enable_audit_fix_fallback)
@@ -351,7 +354,7 @@ module Dependabot
351
354
  # npm audit fix exits non-zero when vulnerabilities remain, so we
352
355
  # rescue and use whatever lockfile changes it managed to make.
353
356
  begin
354
- NativeHelpers.run_npm_audit_fix_command
357
+ NativeHelpers.run_npm_audit_fix_command(security_updates_only: security_updates_only?)
355
358
  sub_dependencies.each { |dep| dep.metadata[:audit_fix_used] = true }
356
359
  rescue SharedHelpers::HelperSubprocessFailed
357
360
  Dependabot.logger.info("npm audit fix failed or partially fixed — continuing with any changes made")
@@ -400,10 +400,17 @@ module Dependabot
400
400
  end
401
401
 
402
402
  # Run single pnpm command returning stdout/stderr
403
- sig { params(command: String, fingerprint: T.nilable(String)).returns(String) }
404
- def self.run_pnpm_command(command, fingerprint: nil)
403
+ sig do
404
+ params(
405
+ command: String,
406
+ fingerprint: T.nilable(String),
407
+ env: T.nilable(T::Hash[String, String])
408
+ ).returns(String)
409
+ end
410
+ def self.run_pnpm_command(command, fingerprint: nil, env: nil)
405
411
  if Dependabot::Experiments.enabled?(:enable_corepack_for_npm_and_yarn)
406
- package_manager_run_command(PNPMPackageManager::NAME, command, fingerprint: fingerprint)
412
+ merged_env = merge_corepack_env(env)
413
+ package_manager_run_command(PNPMPackageManager::NAME, command, fingerprint: fingerprint, env: merged_env)
407
414
  else
408
415
  Dependabot::SharedHelpers.run_shell_command(
409
416
  "pnpm #{command}",
@@ -422,7 +429,8 @@ module Dependabot
422
429
  end
423
430
  def self.run_single_yarn_command(command, fingerprint: nil, env: nil)
424
431
  if Dependabot::Experiments.enabled?(:enable_corepack_for_npm_and_yarn)
425
- package_manager_run_command(YarnPackageManager::NAME, command, fingerprint: fingerprint, env: env)
432
+ merged_env = merge_corepack_env(env)
433
+ package_manager_run_command(YarnPackageManager::NAME, command, fingerprint: fingerprint, env: merged_env)
426
434
  else
427
435
  Dependabot::SharedHelpers.run_shell_command(
428
436
  "yarn #{command}",
@@ -21,40 +21,49 @@ module Dependabot
21
21
  File.join(__dir__, "../../../helpers")
22
22
  end
23
23
 
24
- sig { params(dependency_names: T::Array[String]).returns(String) }
25
- def self.run_npm8_subdependency_update_command(dependency_names)
24
+ sig { params(dependency_names: T::Array[String], security_updates_only: T::Boolean).returns(String) }
25
+ def self.run_npm8_subdependency_update_command(dependency_names, security_updates_only: false)
26
26
  # NOTE: npm options
27
27
  # - `--force` ignores checks for platform (os, cpu) and engines
28
28
  # - `--ignore-scripts` disables prepare and prepack scripts which are run
29
29
  # when installing git dependencies
30
- command = [
30
+ command_args = [
31
31
  "update",
32
32
  *dependency_names,
33
33
  "--force",
34
34
  "--ignore-scripts",
35
35
  "--package-lock-only"
36
- ].join(" ")
36
+ ]
37
+ # Override any min-release-age set in .npmrc: security fixes must not be
38
+ # blocked by a release-age gate the user configured for regular updates.
39
+ command_args << "--min-release-age=0" if security_updates_only
40
+ command = command_args.join(" ")
37
41
 
38
- fingerprint = [
42
+ fingerprint_args = [
39
43
  "update",
40
44
  "<dependency_names>",
41
45
  "--force",
42
46
  "--ignore-scripts",
43
47
  "--package-lock-only"
44
- ].join(" ")
48
+ ]
49
+ fingerprint_args << "--min-release-age=0" if security_updates_only
50
+ fingerprint = fingerprint_args.join(" ")
45
51
 
46
52
  Helpers.run_npm_command(command, fingerprint: fingerprint)
47
53
  end
48
54
 
49
- sig { returns(String) }
50
- def self.run_npm_audit_fix_command
55
+ sig { params(security_updates_only: T::Boolean).returns(String) }
56
+ def self.run_npm_audit_fix_command(security_updates_only: false)
51
57
  # Fallback for transitive dependencies in workspace repos where
52
58
  # `npm update` is a no-op because the package isn't in package.json.
53
59
  # `npm audit fix` updates all fixable vulnerabilities in the lockfile.
54
60
  # `--force` ignores checks for platform (os, cpu) and engines,
55
61
  # matching the flags used by run_npm8_subdependency_update_command.
56
62
  command = "audit fix --force --package-lock-only --ignore-scripts"
57
- fingerprint = "audit fix --force --package-lock-only --ignore-scripts"
63
+ # Override any min-release-age set in .npmrc: security fixes must not be
64
+ # blocked by a release-age gate the user configured for regular updates.
65
+ command += " --min-release-age=0" if security_updates_only
66
+ fingerprint = command
58
67
 
59
68
  Helpers.run_npm_command(command, fingerprint: fingerprint)
60
69
  end
@@ -11,6 +11,7 @@ require "dependabot/npm_and_yarn/sub_dependency_files_filterer"
11
11
  require "dependabot/npm_and_yarn/update_checker"
12
12
  require "dependabot/npm_and_yarn/update_checker/dependency_files_builder"
13
13
  require "dependabot/npm_and_yarn/version"
14
+ require "dependabot/security_advisory"
14
15
  require "dependabot/shared_helpers"
15
16
  require "sorbet-runtime"
16
17
 
@@ -38,6 +39,9 @@ module Dependabot
38
39
  sig { returns(T.nilable(String)) }
39
40
  attr_reader :repo_contents_path
40
41
 
42
+ sig { returns(T::Array[Dependabot::SecurityAdvisory]) }
43
+ attr_reader :security_advisories
44
+
41
45
  sig do
42
46
  params(
43
47
  dependency: Dependency,
@@ -45,7 +49,8 @@ module Dependabot
45
49
  dependency_files: T::Array[Dependabot::DependencyFile],
46
50
  ignored_versions: T::Array[String],
47
51
  latest_allowable_version: T.nilable(T.any(String, Gem::Version)),
48
- repo_contents_path: T.nilable(String)
52
+ repo_contents_path: T.nilable(String),
53
+ security_advisories: T::Array[Dependabot::SecurityAdvisory]
49
54
  ).void
50
55
  end
51
56
  def initialize(
@@ -54,7 +59,8 @@ module Dependabot
54
59
  dependency_files:,
55
60
  ignored_versions:,
56
61
  latest_allowable_version:,
57
- repo_contents_path:
62
+ repo_contents_path:,
63
+ security_advisories: []
58
64
  )
59
65
  @dependency = dependency
60
66
  @credentials = credentials
@@ -62,6 +68,7 @@ module Dependabot
62
68
  @ignored_versions = ignored_versions
63
69
  @latest_allowable_version = latest_allowable_version
64
70
  @repo_contents_path = repo_contents_path
71
+ @security_advisories = security_advisories
65
72
  end
66
73
 
67
74
  sig { returns(T.nilable(T.any(String, Gem::Version))) }
@@ -321,12 +328,15 @@ module Dependabot
321
328
  Dir.chdir(path) do
322
329
  original_content = File.read(lockfile_name)
323
330
 
324
- NativeHelpers.run_npm8_subdependency_update_command([dependency.name])
331
+ NativeHelpers.run_npm8_subdependency_update_command(
332
+ [dependency.name],
333
+ security_updates_only: security_updates_only?
334
+ )
325
335
 
326
336
  updated_content = File.read(lockfile_name)
327
337
  if updated_content == original_content && Dependabot::Experiments.enabled?(:enable_audit_fix_fallback)
328
338
  begin
329
- NativeHelpers.run_npm_audit_fix_command
339
+ NativeHelpers.run_npm_audit_fix_command(security_updates_only: security_updates_only?)
330
340
  dependency.metadata[:audit_fix_used] = true
331
341
  rescue SharedHelpers::HelperSubprocessFailed
332
342
  Dependabot.logger.info("npm audit fix failed or partially fixed — continuing with any changes made")
@@ -357,6 +367,14 @@ module Dependabot
357
367
  dependency.version_class
358
368
  end
359
369
 
370
+ # Security fixes must not be blocked by a min-release-age gate the user
371
+ # configured in .npmrc for regular updates, so the native npm commands
372
+ # are invoked with --min-release-age=0 when resolving a security fix.
373
+ sig { returns(T::Boolean) }
374
+ def security_updates_only?
375
+ security_advisories.any?
376
+ end
377
+
360
378
  sig { returns(Dependabot::Dependency) }
361
379
  def updated_dependency
362
380
  Dependabot::Dependency.new(
@@ -483,7 +483,8 @@ module Dependabot
483
483
  dependency_files: dependency_files,
484
484
  ignored_versions: ignored_versions,
485
485
  latest_allowable_version: latest_version,
486
- repo_contents_path: repo_contents_path
486
+ repo_contents_path: repo_contents_path,
487
+ security_advisories: security_advisories
487
488
  )
488
489
  end
489
490
 
@@ -582,6 +583,12 @@ module Dependabot
582
583
  # per-package filtering.
583
584
  sig { void }
584
585
  def apply_npmrc_min_release_age
586
+ # Security fixes must not be blocked by a release-age gate the user
587
+ # configured for regular updates. npm install/update is invoked with
588
+ # --min-release-age=0 for security updates, so the cooldown floor would
589
+ # only filter out the security fix version at selection time.
590
+ return if security_update?
591
+
585
592
  npmrc_days = npmrc_min_release_age_days
586
593
  return unless npmrc_days&.positive?
587
594
 
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.383.0
4
+ version: 0.384.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.383.0
18
+ version: 0.384.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.383.0
25
+ version: 0.384.0
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: debug
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -377,7 +377,7 @@ licenses:
377
377
  - MIT
378
378
  metadata:
379
379
  bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
380
- changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.383.0
380
+ changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.384.0
381
381
  rdoc_options: []
382
382
  require_paths:
383
383
  - lib
@@ -392,7 +392,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
392
392
  - !ruby/object:Gem::Version
393
393
  version: 3.3.0
394
394
  requirements: []
395
- rubygems_version: 3.7.2
395
+ rubygems_version: 4.0.14
396
396
  specification_version: 4
397
397
  summary: Provides Dependabot support for Javascript (npm and yarn)
398
398
  test_files: []