diff --git a/bin/dry-run.rb b/bin/dry-run.rb index af1c1e70db7..a80a6131efd 100755 --- a/bin/dry-run.rb +++ b/bin/dry-run.rb @@ -732,7 +732,10 @@ def security_fix?(dependency) next end - updater = file_updater_for(updated_deps) + # Removal is only supported for transitive dependencies which are removed as a + # side effect of the parent update + deps_to_update = updated_deps.reject{ |d| d.removed? } + updater = file_updater_for(deps_to_update) updated_files = updater.updated_dependency_files # Currently unused but used to create pull requests (from the updater) diff --git a/common/lib/dependabot/dependency.rb b/common/lib/dependabot/dependency.rb index e5f6338aba8..5dffd063a17 100644 --- a/common/lib/dependabot/dependency.rb +++ b/common/lib/dependabot/dependency.rb @@ -41,7 +41,7 @@ def self.register_name_normaliser(package_manager, name_builder) def initialize(name:, requirements:, package_manager:, version: nil, previous_version: nil, previous_requirements: nil, - subdependency_metadata: []) + subdependency_metadata: [], removed: false) @name = name @version = version @requirements = requirements.map { |req| symbolize_keys(req) } @@ -53,6 +53,7 @@ def initialize(name:, requirements:, package_manager:, version: nil, @subdependency_metadata = subdependency_metadata&. map { |h| symbolize_keys(h) } end + @removed = removed check_values end @@ -61,6 +62,10 @@ def top_level? requirements.any? end + def removed? + @removed + end + def to_h { "name" => name, @@ -69,7 +74,8 @@ def to_h "previous_version" => previous_version, "previous_requirements" => previous_requirements, "package_manager" => package_manager, - "subdependency_metadata" => subdependency_metadata + "subdependency_metadata" => subdependency_metadata, + "removed" => removed? ? true : nil }.compact end diff --git a/common/lib/dependabot/pull_request_creator/message_builder.rb b/common/lib/dependabot/pull_request_creator/message_builder.rb index 08ea7ed1bea..ef19f4fad08 100644 --- a/common/lib/dependabot/pull_request_creator/message_builder.rb +++ b/common/lib/dependabot/pull_request_creator/message_builder.rb @@ -293,10 +293,14 @@ def metadata_links return metadata_links_for_dep(dependencies.first) if dependencies.count == 1 dependencies.map do |dep| - "\n\nUpdates `#{dep.display_name}` "\ - "#{from_version_msg(previous_version(dep))}to "\ - "#{new_version(dep)}"\ - "#{metadata_links_for_dep(dep)}" + if dep.removed? + "\n\nRemoves `#{dep.display_name}`" + else + "\n\nUpdates `#{dep.display_name}` "\ + "#{from_version_msg(previous_version(dep))}to "\ + "#{new_version(dep)}"\ + "#{metadata_links_for_dep(dep)}" + end end.join end @@ -313,9 +317,13 @@ def metadata_cascades return metadata_cascades_for_dep(dependencies.first) if dependencies.one? dependencies.map do |dep| - msg = "\nUpdates `#{dep.display_name}` "\ - "#{from_version_msg(previous_version(dep))}"\ - "to #{new_version(dep)}" + msg = if dep.removed? + "\nRemoves `#{dep.display_name}`" + else + "\nUpdates `#{dep.display_name}` "\ + "#{from_version_msg(previous_version(dep))}"\ + "to #{new_version(dep)}" + end if vulnerabilities_fixed[dep.name]&.one? msg += " **This update includes a security fix.**" @@ -328,6 +336,8 @@ def metadata_cascades end def metadata_cascades_for_dep(dependency) + return "" if dependency.removed? + MetadataPresenter.new( dependency: dependency, source: source, diff --git a/common/lib/dependabot/security_advisory.rb b/common/lib/dependabot/security_advisory.rb index 23622cc0449..86e1ba27170 100644 --- a/common/lib/dependabot/security_advisory.rb +++ b/common/lib/dependabot/security_advisory.rb @@ -62,6 +62,9 @@ def fixed_by?(dependency) # Ignore deps that weren't previously vulnerable return false unless affects_version?(dependency.previous_version) + # Removing a dependency is a way to fix the vulnerability + return true if dependency.removed? + # Select deps that are now fixed !affects_version?(dependency.version) end diff --git a/common/spec/dependabot/dependency_spec.rb b/common/spec/dependabot/dependency_spec.rb index e7d92ac153c..47d7454baa6 100644 --- a/common/spec/dependabot/dependency_spec.rb +++ b/common/spec/dependabot/dependency_spec.rb @@ -217,6 +217,27 @@ is_expected.to eq(expected) end end + + context "when removed" do + let(:dependency_args) do + { + name: "dep", + requirements: [], + package_manager: "dummy", + removed: true + } + end + + it do + expected = { + "name" => "dep", + "package_manager" => "dummy", + "requirements" => [], + "removed" => true + } + is_expected.to eq(expected) + end + end end describe "#subdependency_metadata" do diff --git a/common/spec/dependabot/pull_request_creator/message_builder_spec.rb b/common/spec/dependabot/pull_request_creator/message_builder_spec.rb index adfb93ccf8d..c0d78ec6bcd 100644 --- a/common/spec/dependabot/pull_request_creator/message_builder_spec.rb +++ b/common/spec/dependabot/pull_request_creator/message_builder_spec.rb @@ -1531,6 +1531,45 @@ def commits_details(base:, head:) end end + context "removing a transitive dependency" do + let(:dependencies) { [removed_dependency, dependency] } + let(:removed_dependency) do + Dependabot::Dependency.new( + name: "statesman", + previous_version: "1.6.0", + package_manager: "dummy", + requirements: [], + previous_requirements: [], + removed: true + ) + end + + it "includes details of both dependencies" do + expect(pr_message). + to eq( + "Bumps [statesman](https://github.com/gocardless/statesman) "\ + "and [business](https://github.com/gocardless/business). "\ + "These dependencies needed to be updated together.\n"\ + "Removes `statesman`\n"\ + "Updates `business` from 1.4.0 to 1.5.0\n"\ + "\n"\ + "Changelog\n"\ + "Sourced from "\ + "business's changelog.\n"\ + "\n"\ + "1.5.0 - June 2, 2015\n"\ + "\n"\ + "Add 2016 holiday definitions\n"\ + "\n"\ + "\n"\ + "\n"\ + "#{commits_details(base: 'v1.4.0', head: 'v1.5.0')}"\ + "\n" + ) + end + end + context "with multiple git source requirements", :vcr do include_context "with multiple git sources" diff --git a/common/spec/dependabot/security_advisory_spec.rb b/common/spec/dependabot/security_advisory_spec.rb index d83e557b7b1..41b812c6681 100644 --- a/common/spec/dependabot/security_advisory_spec.rb +++ b/common/spec/dependabot/security_advisory_spec.rb @@ -131,7 +131,8 @@ version: dependency_version, previous_version: dependency_previous_version, requirements: [], - previous_requirements: [] + previous_requirements: [], + removed: removed ) end let(:package_manager) { "dummy" } @@ -140,6 +141,7 @@ let(:safe_versions) { [Gem::Requirement.new("~> 1.11.0")] } let(:dependency_version) { "1.11.1" } let(:dependency_previous_version) { "0.7.1" } + let(:removed) { false } it { is_expected.to eq(true) } @@ -183,6 +185,12 @@ it { is_expected.to eq(false) } end end + + context "with a removed dependency" do + let(:dependency_version) { nil } + let(:removed) { true } + it { is_expected.to eq(true) } + end end describe "#affects_version?" do diff --git a/npm_and_yarn/lib/dependabot/npm_and_yarn/update_checker.rb b/npm_and_yarn/lib/dependabot/npm_and_yarn/update_checker.rb index 88dd5028495..13ed0a3e3f4 100644 --- a/npm_and_yarn/lib/dependabot/npm_and_yarn/update_checker.rb +++ b/npm_and_yarn/lib/dependabot/npm_and_yarn/update_checker.rb @@ -111,7 +111,8 @@ def vulnerability_audit @vulnerability_audit ||= VulnerabilityAuditor.new( dependency_files: dependency_files, - credentials: credentials + credentials: credentials, + allow_removal: @options.key?(:npm_transitive_dependency_removal) ).audit( dependency: dependency, security_advisories: security_advisories @@ -141,6 +142,7 @@ def updated_dependencies_after_full_unlock map { |update_details| build_updated_dependency(update_details) } end + # rubocop:disable Metrics/AbcSize def conflicting_updated_dependencies top_level_dependencies = top_level_dependency_lookup @@ -148,27 +150,29 @@ def conflicting_updated_dependencies vulnerability_audit["fix_updates"].each do |update| dependency_name = update["dependency_name"] requirements = top_level_dependencies[dependency_name]&.requirements || [] - conflicting_dep = Dependency.new( - name: dependency_name, - package_manager: "npm_and_yarn", - requirements: requirements - ) updated_deps << build_updated_dependency( - dependency: conflicting_dep, + dependency: Dependency.new( + name: dependency_name, + package_manager: "npm_and_yarn", + requirements: requirements + ), version: update["target_version"], previous_version: update["current_version"] ) end + # rubocop:enable Metrics/AbcSize # We don't need to update this but need to include it so it's described # in the PR and we'll pass validation that this dependency is at a # non-vulnerable version. if updated_deps.none? { |dep| dep.name == dependency.name } + target_version = vulnerability_audit["target_version"] updated_deps << build_updated_dependency( dependency: dependency, - version: vulnerability_audit["target_version"], - previous_version: dependency.version + version: target_version, + previous_version: dependency.version, + removed: target_version.nil? ) end @@ -189,7 +193,8 @@ def top_level_dependency_lookup def build_updated_dependency(update_details) original_dep = update_details.fetch(:dependency) - version = update_details.fetch(:version).to_s + removed = update_details.fetch(:removed, false) + version = update_details.fetch(:version).to_s unless removed previous_version = update_details.fetch(:previous_version)&.to_s Dependency.new( @@ -203,7 +208,8 @@ def build_updated_dependency(update_details) ).updated_requirements, previous_version: previous_version, previous_requirements: original_dep.requirements, - package_manager: original_dep.package_manager + package_manager: original_dep.package_manager, + removed: removed ) end diff --git a/npm_and_yarn/lib/dependabot/npm_and_yarn/update_checker/vulnerability_auditor.rb b/npm_and_yarn/lib/dependabot/npm_and_yarn/update_checker/vulnerability_auditor.rb index 3a4bc9acf12..2c722842497 100644 --- a/npm_and_yarn/lib/dependabot/npm_and_yarn/update_checker/vulnerability_auditor.rb +++ b/npm_and_yarn/lib/dependabot/npm_and_yarn/update_checker/vulnerability_auditor.rb @@ -15,9 +15,10 @@ module Dependabot module NpmAndYarn class UpdateChecker < Dependabot::UpdateCheckers::Base class VulnerabilityAuditor - def initialize(dependency_files:, credentials:) + def initialize(dependency_files:, credentials:, allow_removal: false) @dependency_files = dependency_files @credentials = credentials + @allow_removal = allow_removal end # Finds any dependencies in the `package-lock.json` or `npm-shrinkwrap.json` that have @@ -96,7 +97,7 @@ def viable_audit_result?(audit_result, security_advisories) def validate_audit_result(audit_result, security_advisories) return :fix_unavailable unless audit_result["fix_available"] - return :vulnerable_dependency_removed if vulnerable_dependency_removed?(audit_result) + return :vulnerable_dependency_removed if !@allow_removal && vulnerable_dependency_removed?(audit_result) return :dependency_still_vulnerable if dependency_still_vulnerable?(audit_result, security_advisories) return :downgrades_dependencies if downgrades_dependencies?(audit_result) @@ -108,6 +109,9 @@ def vulnerable_dependency_removed?(audit_result) end def dependency_still_vulnerable?(audit_result, security_advisories) + # vulnerable depenendency is removed if the target version is nil + return false unless audit_result["target_version"] + version = Version.new(audit_result["target_version"]) security_advisories.any? { |a| a.vulnerable?(version) } end @@ -121,6 +125,8 @@ def downgrades_dependencies?(audit_result) end def downgrades_version?(current_version, target_version) + return false unless target_version + current = Version.new(current_version) target = Version.new(target_version) current > target diff --git a/npm_and_yarn/spec/dependabot/npm_and_yarn/update_checker/vulnerability_auditor_spec.rb b/npm_and_yarn/spec/dependabot/npm_and_yarn/update_checker/vulnerability_auditor_spec.rb index 372e921b2a7..83de366e4c0 100644 --- a/npm_and_yarn/spec/dependabot/npm_and_yarn/update_checker/vulnerability_auditor_spec.rb +++ b/npm_and_yarn/spec/dependabot/npm_and_yarn/update_checker/vulnerability_auditor_spec.rb @@ -16,9 +16,10 @@ "password" => "token" }] end + let(:allow_removal) { true } subject do - described_class.new(dependency_files: dependency_files, credentials: credentials) + described_class.new(dependency_files: dependency_files, credentials: credentials, allow_removal: allow_removal) end describe "#audit" do @@ -69,7 +70,7 @@ context "when a fix removes the vulnerable dependency" do let(:dependency_files) { project_dependency_files("npm8/locked_transitive_dependency_removed") } - it "returns fix_available => false" do + it "omits target_version to indicate removal" do security_advisories = [ Dependabot::SecurityAdvisory.new( dependency_name: dependency.name, @@ -79,9 +80,38 @@ ) ] - expect(Dependabot.logger).to receive(:info).with(/audit result not viable: vulnerable_dependency_removed/i) expect(subject.audit(dependency: dependency, security_advisories: security_advisories)). - to include("fix_available" => false) + to include( + "dependency_name" => dependency.name, + "current_version" => "1.0.0", + "fix_available" => true, + "fix_updates" => [{ + "dependency_name" => "@dependabot-fixtures/npm-remove-dependency", + "current_version" => "10.0.0", + "target_version" => "10.0.1", + "top_level_ancestors" => [] + }], + "top_level_ancestors" => ["@dependabot-fixtures/npm-remove-dependency"] + ) + end + + context "when removal is disabled" do + let(:allow_removal) { false } + + it "returns fix_available => false" do + security_advisories = [ + Dependabot::SecurityAdvisory.new( + dependency_name: dependency.name, + package_manager: "npm_and_yarn", + vulnerable_versions: ["<1.0.1"], + safe_versions: ["1.0.1"] + ) + ] + + expect(Dependabot.logger).to receive(:info).with(/audit result not viable: vulnerable_dependency_removed/i) + expect(subject.audit(dependency: dependency, security_advisories: security_advisories)). + to include("fix_available" => false) + end end end diff --git a/npm_and_yarn/spec/dependabot/npm_and_yarn/update_checker_spec.rb b/npm_and_yarn/spec/dependabot/npm_and_yarn/update_checker_spec.rb index 4bfe68b4f49..98a043cef76 100644 --- a/npm_and_yarn/spec/dependabot/npm_and_yarn/update_checker_spec.rb +++ b/npm_and_yarn/spec/dependabot/npm_and_yarn/update_checker_spec.rb @@ -204,7 +204,7 @@ let(:dependency_files) { project_dependency_files("npm8/locked_transitive_dependency_removed") } let(:registry_listing_url) { "https://registry.npmjs.org/locked_transitive_dependency_removed" } - it "doesn't allow an update yet" do + it "doesn't allow an update because removal has not been enabled" do expect(checker.can_update?(requirements_to_unlock: :all)).to eq(false) end end @@ -1443,6 +1443,51 @@ ) end end + + context "when the vulnerable transitive dependency is removed as a result of updating its parent" do + let(:dependency_files) { project_dependency_files("npm8/locked_transitive_dependency_removed") } + let(:registry_listing_url) { "https://registry.npmjs.org/locked-transitive-dependency-removed" } + let(:options) do + { + npm_transitive_security_updates: true, + npm_transitive_dependency_removal: true + } + end + + it "correctly updates the parent dependency and removes the transitive because removal is enabled" do + expect(checker.send(:updated_dependencies_after_full_unlock)).to contain_exactly( + Dependabot::Dependency.new( + name: "@dependabot-fixtures/npm-transitive-dependency", + package_manager: "npm_and_yarn", + previous_requirements: [], + previous_version: "1.0.0", + requirements: [], + removed: true + ), + Dependabot::Dependency.new( + name: "@dependabot-fixtures/npm-remove-dependency", + package_manager: "npm_and_yarn", + previous_requirements: [{ + requirement: "10.0.0", + file: "package.json", + groups: ["dependencies"], + source: { + type: "registry", + url: "https://registry.npmjs.org" + } + }], + previous_version: "10.0.0", + requirements: [{ + requirement: "10.0.1", + file: "package.json", + groups: ["dependencies"], + source: nil + }], + version: "10.0.1" + ) + ) + end + end end end diff --git a/npm_and_yarn/spec/fixtures/vcr_cassettes/Dependabot_NpmAndYarn_UpdateChecker/_can_update_/given_an_up-to-date_dependency/for_a_locked_transitive_security_update_with_npm_transitive_security_updates_enabled/when_the_vulnerable_transitive_dependency_is_removed_as_a_result_of_updating_its_parent/doesn_t_allow_an_update_because_removal_has_not_been_enabled.yml b/npm_and_yarn/spec/fixtures/vcr_cassettes/Dependabot_NpmAndYarn_UpdateChecker/_can_update_/given_an_up-to-date_dependency/for_a_locked_transitive_security_update_with_npm_transitive_security_updates_enabled/when_the_vulnerable_transitive_dependency_is_removed_as_a_result_of_updating_its_parent/doesn_t_allow_an_update_because_removal_has_not_been_enabled.yml new file mode 100644 index 00000000000..a8ce2812686 --- /dev/null +++ b/npm_and_yarn/spec/fixtures/vcr_cassettes/Dependabot_NpmAndYarn_UpdateChecker/_can_update_/given_an_up-to-date_dependency/for_a_locked_transitive_security_update_with_npm_transitive_security_updates_enabled/when_the_vulnerable_transitive_dependency_is_removed_as_a_result_of_updating_its_parent/doesn_t_allow_an_update_because_removal_has_not_been_enabled.yml @@ -0,0 +1,101 @@ +--- +http_interactions: +- request: + method: get + uri: https://registry.npmjs.org/@dependabot-fixtures%2Fnpm-transitive-dependency + body: + encoding: US-ASCII + string: '' + headers: + user-agent: + - dependabot-core/0.196.2 excon/0.92.4 ruby/2.7.5 (x86_64-linux-gnu) (+https://github.com/dependabot/dependabot-core) + response: + status: + code: 200 + message: OK + headers: + date: + - Thu, 28 Jul 2022 21:52:20 GMT + content-type: + - application/json + connection: + - keep-alive + cf-ray: + - 7320d75e9fa796e7-SJC + access-control-allow-origin: + - "*" + cache-control: + - public, max-age=300 + etag: + - W/"bec53a4510574acfeb4ed10a4ac2fafe" + last-modified: + - Fri, 10 Jun 2022 17:08:32 GMT + vary: + - accept-encoding, accept + cf-cache-status: + - MISS + expect-ct: + - max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct" + x-amz-replication-status: + - COMPLETED + server: + - cloudflare + content-encoding: + - '' + body: + encoding: UTF-8 + string: '{"_id":"@dependabot-fixtures/npm-transitive-dependency","_rev":"2-a24e904986d72cc37c235292711c96f8","name":"@dependabot-fixtures/npm-transitive-dependency","dist-tags":{"latest":"1.0.1"},"versions":{"1.0.0":{"name":"@dependabot-fixtures/npm-transitive-dependency","version":"1.0.0","description":"a + test fixture for testing transitive dependency updates","main":"index.js","scripts":{"test":"echo + \"Error: no test specified\" && exit 1"},"repository":{"type":"git","url":"git+https://github.com/dependabot-fixtures/npm-transitive-dependency.git"},"author":"","license":"ISC","bugs":{"url":"https://github.com/dependabot-fixtures/npm-transitive-dependency/issues"},"homepage":"https://github.com/dependabot-fixtures/npm-transitive-dependency#readme","gitHead":"ed979391400cfecc58469424e6db9601a5a09e36","_id":"@dependabot-fixtures/npm-transitive-dependency@1.0.0","_nodeVersion":"16.15.0","_npmVersion":"8.5.5","dist":{"integrity":"sha512-nFbzQH0TRgdzSA2/FH6MPnxZDpD+5Bgz00aD5Edgbc1wY/k8VC9s7lnk22dBTgJLwoY7MgbrnAf9rAvN08hHVg==","shasum":"b70e2381996ba06233c2a703c73f9ef3674beb2c","tarball":"https://registry.npmjs.org/@dependabot-fixtures/npm-transitive-dependency/-/npm-transitive-dependency-1.0.0.tgz","fileCount":2,"unpackedSize":688,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCgpRAyARsDA1NfJvzQs1/rsk3Nfbbf2fmNynsoPjU8DwIgSC0qB6Vg7C+BFzN/kkl+mKl0AzihRTUZZoz2PNSmx1Y="}],"npm-signature":"-----BEGIN + PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJioR9nACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrXBA/6AuhKeO/ZWLXnw2NO4IGPQ/maGX6wTbW0M0CQEsJo7S0ORbzL\r\nGFrNMF2hyqUnshj3GjaWdZu2fT9S70Pze93eQMAai+dU2kQPTvYtMysMWT1U\r\nKhhj26cS0fmJzK8DjZwfEfhAl2BVTVxulivAsTVTKeBeu/tIOCW3GXquw3kb\r\n/AqyNxPBl33sjOjH2LSEUgE4YKsL8wIN1/4HKn0sXqfDBN/X+4eBjdVQjpON\r\nAHNOZKdF6pxkw5q7dYdoMenx7MGDrowDCtMVrZDjl1SRW5k+CY3zTOrosgAJ\r\nYUMUll13oYL9GLT61zZECFVIaF2RIULgklnLy2REHqu1fyE8uzA7305hE/PB\r\nEew0tR0cPQHeczgBUGs3V7Q5ZNKcEGj2cVycmXmN7dRNKEoq6PxgLGPN1sOo\r\nvdUnC9m8Vt0bqdhXorlk5WTIqXRFgIXw5ahoyAXGMcD3UCLnr/E2Udp/LcoN\r\nq5ZDg6OoEvgikM5vlcVTBbKILpc689bkem2dY7EGYrrwdpB/DEsg8KV0aoYP\r\nW7SdtbGxQ+nCBQKzx2igBmk62neSzKxWZxJo+GXSf8dJnkbPWkRZLFaUoRLO\r\nJGEdtUqKcl3gagmTgOTlNEj2CBMs2wmab13dMmwXBnKn4/Lokzrzqw4uZCbr\r\n0Z5ciR7Xqsjrdzpi/74XHyS9FHDr+eUulfU=\r\n=JwaA\r\n-----END + PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mctofu","email":"mctofu@github.com"},"directories":{},"maintainers":[{"name":"nishnha","email":"nishnha@gmail.com"},{"name":"mctofu","email":"mctofu@github.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/npm-transitive-dependency_1.0.0_1654726503635_0.2532339791507354"},"_hasShrinkwrap":false},"1.0.1":{"name":"@dependabot-fixtures/npm-transitive-dependency","version":"1.0.1","description":"a + test fixture for testing transitive dependency updates","main":"index.js","scripts":{"test":"echo + \"Error: no test specified\" && exit 1"},"repository":{"type":"git","url":"git+https://github.com/dependabot-fixtures/npm-transitive-dependency.git"},"author":"","license":"ISC","bugs":{"url":"https://github.com/dependabot-fixtures/npm-transitive-dependency/issues"},"homepage":"https://github.com/dependabot-fixtures/npm-transitive-dependency#readme","gitHead":"89457a1a5b8071ca9f602bed33fd869532db9ad9","_id":"@dependabot-fixtures/npm-transitive-dependency@1.0.1","_nodeVersion":"16.15.0","_npmVersion":"8.5.5","dist":{"integrity":"sha512-nWQzJEqSqKZu+mgNSVdsO69NG6vCGIN9FuM+Vip5nqItqrNeQoITZM6/q6+tqgdM48XkQEOUpEiYpAdoMbxniw==","shasum":"fe20ae5230674c08fa5600c9cfb8b72ea3e7e066","tarball":"https://registry.npmjs.org/@dependabot-fixtures/npm-transitive-dependency/-/npm-transitive-dependency-1.0.1.tgz","fileCount":2,"unpackedSize":688,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCA7kCA+qIaCh9QdYfVdHznok0CCgEaC8ssZMY74avehAIhAOLg3oYmjuIibpM3aaE6PbQ9H9d2Zs0IeDpFCMETSPyo"}],"npm-signature":"-----BEGIN + PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJioSCiACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo+Og//YEfPtruI3VkwmFKSzIp+WUUScfZm1RK5V9U+04j9re8a7JCh\r\n5yY4H/YjIYGQg8p0yDfdGVk8Hoj6Kh0fdOD8sSziy1sspA1kx3w+6s18FX0n\r\nc/JeO5xhLAdx0vNVYZjNLxo9QEHLQ6kSsglIgxbmj+T6QAjAZ/obMNA+roOd\r\nq/GyU4/6Hw+RCTDQttRMKHmcxy7GZe4Mvb12M14kK+2kbSs4rKqepa1+0MH+\r\nAlRbRoKPIaqtu4jWTl1UFMnU5v2qMyOFUEI77JVSqK9TeFlt8ShrC+EZIgAT\r\nZI+Lrucz5zNgu4nGKEQoqMcETmKLBTqsa4HB7Aras8O4bEuNsJ24GvQb0JRh\r\nsT5BCP9yknWNf1LW04Bc1yRI058PDWYE0Sk4voNkR7P37P2OpLhVhWiIy90u\r\neT6Hj3uNGYnJaCIShpaiTGZXkVVseW6rJjf2I+otKXqlkpxQaE/tXfe5QclF\r\nVDcjGVzk9PhWgdjVxfIymVDGawmuQ0wdLV7FEOc4NBxnzCf+NfDjKVVu5V+f\r\nCiO8moDW10WULrtq7YtAJ1RL9lj3YcT8wcALGxytV5Fcz4Mu6IFdDuGRdO4w\r\nToPP0Kzqhc2veGAMKMwGdwP6vyHDACsYWp5tPYi6Lib3iHayWggzHhmGrpUE\r\noBZhS1Gglw9QBM+QE+pC+VeQsb3wQhIC/JM=\r\n=zgHz\r\n-----END + PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mctofu","email":"mctofu@github.com"},"directories":{},"maintainers":[{"name":"nishnha","email":"nishnha@gmail.com"},{"name":"mctofu","email":"mctofu@github.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/npm-transitive-dependency_1.0.1_1654726818213_0.4396995531588652"},"_hasShrinkwrap":false}},"time":{"created":"2022-06-08T22:15:03.583Z","1.0.0":"2022-06-08T22:15:03.799Z","modified":"2022-06-10T17:08:31.026Z","1.0.1":"2022-06-08T22:20:18.465Z"},"maintainers":[{"email":"bdragon@github.com","name":"bryandragon"},{"email":"nishnha@gmail.com","name":"nishnha"},{"email":"mctofu@github.com","name":"mctofu"}],"description":"a + test fixture for testing transitive dependency updates","homepage":"https://github.com/dependabot-fixtures/npm-transitive-dependency#readme","repository":{"type":"git","url":"git+https://github.com/dependabot-fixtures/npm-transitive-dependency.git"},"bugs":{"url":"https://github.com/dependabot-fixtures/npm-transitive-dependency/issues"},"license":"ISC","readme":"# + npm-transitive-dependency\nA dependency used by npm-parent-dependency\n","readmeFilename":"README.md"}' + recorded_at: Thu, 28 Jul 2022 21:52:20 GMT +- request: + method: get + uri: https://registry.npmjs.org/@dependabot-fixtures%2Fnpm-transitive-dependency/1.0.1 + body: + encoding: US-ASCII + string: '' + headers: + user-agent: + - dependabot-core/0.196.2 excon/0.92.4 ruby/2.7.5 (x86_64-linux-gnu) (+https://github.com/dependabot/dependabot-core) + response: + status: + code: 200 + message: OK + headers: + date: + - Thu, 28 Jul 2022 21:52:20 GMT + content-type: + - application/json + connection: + - keep-alive + cf-ray: + - 7320d7611ef897db-SJC + access-control-allow-origin: + - "*" + content-encoding: + - '' + vary: + - Accept-Encoding + cf-cache-status: + - DYNAMIC + expect-ct: + - max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct" + server: + - cloudflare + body: + encoding: UTF-8 + string: '{"name":"@dependabot-fixtures/npm-transitive-dependency","version":"1.0.1","description":"a + test fixture for testing transitive dependency updates","main":"index.js","scripts":{"test":"echo + \"Error: no test specified\" && exit 1"},"repository":{"type":"git","url":"git+https://github.com/dependabot-fixtures/npm-transitive-dependency.git"},"author":"","license":"ISC","bugs":{"url":"https://github.com/dependabot-fixtures/npm-transitive-dependency/issues"},"homepage":"https://github.com/dependabot-fixtures/npm-transitive-dependency#readme","gitHead":"89457a1a5b8071ca9f602bed33fd869532db9ad9","_id":"@dependabot-fixtures/npm-transitive-dependency@1.0.1","_nodeVersion":"16.15.0","_npmVersion":"8.5.5","dist":{"integrity":"sha512-nWQzJEqSqKZu+mgNSVdsO69NG6vCGIN9FuM+Vip5nqItqrNeQoITZM6/q6+tqgdM48XkQEOUpEiYpAdoMbxniw==","shasum":"fe20ae5230674c08fa5600c9cfb8b72ea3e7e066","tarball":"https://registry.npmjs.org/@dependabot-fixtures/npm-transitive-dependency/-/npm-transitive-dependency-1.0.1.tgz","fileCount":2,"unpackedSize":688,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCA7kCA+qIaCh9QdYfVdHznok0CCgEaC8ssZMY74avehAIhAOLg3oYmjuIibpM3aaE6PbQ9H9d2Zs0IeDpFCMETSPyo"}],"npm-signature":"-----BEGIN + PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJioSCiACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2Vmo+Og//YEfPtruI3VkwmFKSzIp+WUUScfZm1RK5V9U+04j9re8a7JCh\r\n5yY4H/YjIYGQg8p0yDfdGVk8Hoj6Kh0fdOD8sSziy1sspA1kx3w+6s18FX0n\r\nc/JeO5xhLAdx0vNVYZjNLxo9QEHLQ6kSsglIgxbmj+T6QAjAZ/obMNA+roOd\r\nq/GyU4/6Hw+RCTDQttRMKHmcxy7GZe4Mvb12M14kK+2kbSs4rKqepa1+0MH+\r\nAlRbRoKPIaqtu4jWTl1UFMnU5v2qMyOFUEI77JVSqK9TeFlt8ShrC+EZIgAT\r\nZI+Lrucz5zNgu4nGKEQoqMcETmKLBTqsa4HB7Aras8O4bEuNsJ24GvQb0JRh\r\nsT5BCP9yknWNf1LW04Bc1yRI058PDWYE0Sk4voNkR7P37P2OpLhVhWiIy90u\r\neT6Hj3uNGYnJaCIShpaiTGZXkVVseW6rJjf2I+otKXqlkpxQaE/tXfe5QclF\r\nVDcjGVzk9PhWgdjVxfIymVDGawmuQ0wdLV7FEOc4NBxnzCf+NfDjKVVu5V+f\r\nCiO8moDW10WULrtq7YtAJ1RL9lj3YcT8wcALGxytV5Fcz4Mu6IFdDuGRdO4w\r\nToPP0Kzqhc2veGAMKMwGdwP6vyHDACsYWp5tPYi6Lib3iHayWggzHhmGrpUE\r\noBZhS1Gglw9QBM+QE+pC+VeQsb3wQhIC/JM=\r\n=zgHz\r\n-----END + PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"mctofu","email":"mctofu@github.com"},"directories":{},"maintainers":[{"name":"nishnha","email":"nishnha@gmail.com"},{"name":"mctofu","email":"mctofu@github.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/npm-transitive-dependency_1.0.1_1654726818213_0.4396995531588652"},"_hasShrinkwrap":false}' + recorded_at: Thu, 28 Jul 2022 21:52:20 GMT +recorded_with: VCR 6.1.0
Sourced from "\ + "business's changelog.
\n"\ + "1.5.0 - June 2, 2015\n"\ + "\n"\ + "Add 2016 holiday definitions\n"\ + "\n"\ + "