diff --git a/npm_and_yarn/lib/dependabot/npm_and_yarn/file_updater/npmrc_builder.rb b/npm_and_yarn/lib/dependabot/npm_and_yarn/file_updater/npmrc_builder.rb index e8f524a84a0..ab640953a2b 100644 --- a/npm_and_yarn/lib/dependabot/npm_and_yarn/file_updater/npmrc_builder.rb +++ b/npm_and_yarn/lib/dependabot/npm_and_yarn/file_updater/npmrc_builder.rb @@ -48,7 +48,9 @@ def build_npmrc_content_from_lockfile end def global_registry # rubocop:disable Metrics/PerceivedComplexity - @global_registry ||= + return @global_registry if defined?(@global_registry) + + @global_registry = registry_credentials.find do |cred| next false if CENTRAL_REGISTRIES.include?(cred["registry"]) @@ -132,21 +134,24 @@ def build_npmrc_from_yarnrc def credential_lines_for_npmrc lines = [] registry_credentials.each do |cred| - registry = cred.fetch("registry").sub(%r{\/?$}, "/") + registry = cred.fetch("registry") lines += registry_scopes(registry) if registry_scopes(registry) token = cred.fetch("token", nil) next unless token + # We need to ensure the registry uri ends with a trailing slash in the npmrc file + # but we do not want to add one if it already exists + registry_with_trailing_slash = registry.sub(%r{\/?$}, "/") if token.include?(":") encoded_token = Base64.encode64(token).delete("\n") - lines << "//#{registry}:_auth=#{encoded_token}" + lines << "//#{registry_with_trailing_slash}:_auth=#{encoded_token}" elsif Base64.decode64(token).ascii_only? && Base64.decode64(token).include?(":") - lines << %(//#{registry}:_auth=#{token.delete("\n")}) + lines << %(//#{registry_with_trailing_slash}:_auth=#{token.delete("\n")}) else - lines << "//#{registry}:_authToken=#{token}" + lines << "//#{registry_with_trailing_slash}:_authToken=#{token}" end end @@ -169,7 +174,6 @@ def npmrc_scoped_registries def registry_scopes(registry) # Central registries don't just apply to scopes return if CENTRAL_REGISTRIES.include?(registry) - return unless dependency_urls other_regs = diff --git a/npm_and_yarn/spec/dependabot/npm_and_yarn/file_updater/npmrc_builder_spec.rb b/npm_and_yarn/spec/dependabot/npm_and_yarn/file_updater/npmrc_builder_spec.rb index 1dd9e189374..e9ae9d5cf9a 100644 --- a/npm_and_yarn/spec/dependabot/npm_and_yarn/file_updater/npmrc_builder_spec.rb +++ b/npm_and_yarn/spec/dependabot/npm_and_yarn/file_updater/npmrc_builder_spec.rb @@ -194,7 +194,7 @@ it "adds auth details, and scopes them correctly" do expect(npmrc_content). - to eq("@dependabot:registry=https://npm.fury.io/dependabot/\n"\ + to eq("@dependabot:registry=https://npm.fury.io/dependabot\n"\ "//npm.fury.io/dependabot/:_authToken=my_token\n"\ "//npm.fury.io/dep/:_authToken=my_other_token") end @@ -217,7 +217,7 @@ expect(npmrc_content). to eq( "@dependabot:registry=https://api.bintray.com/npm/"\ - "dependabot/npm-private/\n"\ + "dependabot/npm-private\n"\ "//api.bintray.com/npm/dependabot/"\ "npm-private/:_authToken=my_token" ) @@ -258,7 +258,7 @@ it "adds auth details, and scopes them correctly" do expect(npmrc_content). to eq( - "@dependabot:registry=https://npm.fury.io/dependabot/\n"\ + "@dependabot:registry=https://npm.fury.io/dependabot\n"\ "//npm.fury.io/dependabot/:_authToken=my_token\n"\ "//npm.fury.io/dep/:_authToken=my_other_token" ) @@ -299,7 +299,7 @@ end it "adds auth details, and scopes them correctly" do expect(npmrc_content). - to eq("@dependabot:registry=https://npm.fury.io/dependabot/") + to eq("@dependabot:registry=https://npm.fury.io/dependabot") end end end @@ -570,7 +570,7 @@ end it "adds auth details, and scopes them correctly" do expect(npmrc_content). - to eq("@dependabot:registry=https://npm.fury.io/dependabot/\n"\ + to eq("@dependabot:registry=https://npm.fury.io/dependabot\n"\ "//npm.fury.io/dependabot/:_authToken=my_token") end end @@ -634,7 +634,7 @@ end it "adds auth details, and scopes them correctly" do expect(npmrc_content). - to eq("@dependabot:registry=https://npm.fury.io/dependabot/") + to eq("@dependabot:registry=https://npm.fury.io/dependabot") end end @@ -651,7 +651,7 @@ end it "adds auth details, and scopes them correctly" do expect(npmrc_content). - to eq("@dependabot:registry=https://npm.fury.io/dependabot/") + to eq("@dependabot:registry=https://npm.fury.io/dependabot") end end end @@ -828,5 +828,74 @@ end end end + + context "registry scope generation" do + let(:credentials) do + [{ + "type" => "npm_registry", + "registry" => "registry.npmjs.org" + }, + { + "type" => "npm_registry", + "registry" => "npm.pkg.github.com", + "token" => "my_token" + }] + end + + context "when no packages resolve to the private registry" do + let(:dependency_files) do + project_dependency_files("npm8/simple") + end + + it "adds only the token auth details" do + expect(npmrc_content).to eql("//npm.pkg.github.com/:_authToken=my_token") + end + end + + context "when there are only packages that resolve to the private registry" do + let(:dependency_files) do + project_dependency_files("npm8/private_registry_ghpr_only") + end + + it "adds a global registry line, the scoped registry and token auth details" do + expect(npmrc_content). + to eq(<<~NPMRC.chomp) + registry = https://npm.pkg.github.com + _authToken = my_token + always-auth = true + @dsp-testing:registry=https://npm.pkg.github.com + //npm.pkg.github.com/:_authToken=my_token + NPMRC + end + end + + context "when there are some packages that resolve to the private registry" do + let(:dependency_files) do + project_dependency_files("npm8/private_registry_ghpr_and_npm") + end + + it "adds the scoped registry and token auth details" do + expect(npmrc_content). + to eq(<<~NPMRC.chomp) + @dsp-testing:registry=https://npm.pkg.github.com + //npm.pkg.github.com/:_authToken=my_token + NPMRC + end + end + + context "when there are some packages that resolve to the private registry, but include a port number" do + let(:dependency_files) do + project_dependency_files("npm8/private_registry_ghpr_with_ports") + end + + it "adds the scoped registry and token auth details" do + expect(npmrc_content). + to eq(<<~NPMRC.chomp) + @dsp-testing:registry=https://npm.pkg.github.com + //npm.pkg.github.com/:_authToken=my_token + NPMRC + end + end + end end end diff --git a/npm_and_yarn/spec/fixtures/projects/npm8/private_registry_ghpr_and_npm/package-lock.json b/npm_and_yarn/spec/fixtures/projects/npm8/private_registry_ghpr_and_npm/package-lock.json new file mode 100644 index 00000000000..990e667250c --- /dev/null +++ b/npm_and_yarn/spec/fixtures/projects/npm8/private_registry_ghpr_and_npm/package-lock.json @@ -0,0 +1,40 @@ +{ + "name": "dependabot-consume-private-registries", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "dependabot-consume-private-registries", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@dsp-testing/inner-source-top-secret-npm-2": "1.0.3", + "lodash": "^4.17.20" + } + }, + "node_modules/@dsp-testing/inner-source-top-secret-npm-2": { + "version": "1.0.3", + "resolved": "https://npm.pkg.github.com/download/@dsp-testing/inner-source-top-secret-npm-2/1.0.3/0a19a66110450848d0a88b1be211cadae740a86f0c1c1658ed89c9f391b8f605", + "integrity": "sha512-5Kt5AHgt2qE9YFlRnqizh36k1lcuTdGQP3UsxJgxVUo1Uxh4Z7vDgr7wDBm2hp4PjZ6soE4zupSyfaCbYguQqg==", + "license": "ISC" + }, + "node_modules/lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + } + }, + "dependencies": { + "@dsp-testing/inner-source-top-secret-npm-2": { + "version": "1.0.3", + "resolved": "https://npm.pkg.github.com/download/@dsp-testing/inner-source-top-secret-npm-2/1.0.3/0a19a66110450848d0a88b1be211cadae740a86f0c1c1658ed89c9f391b8f605", + "integrity": "sha512-5Kt5AHgt2qE9YFlRnqizh36k1lcuTdGQP3UsxJgxVUo1Uxh4Z7vDgr7wDBm2hp4PjZ6soE4zupSyfaCbYguQqg==" + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + } + } +} diff --git a/npm_and_yarn/spec/fixtures/projects/npm8/private_registry_ghpr_and_npm/package.json b/npm_and_yarn/spec/fixtures/projects/npm8/private_registry_ghpr_and_npm/package.json new file mode 100644 index 00000000000..0a309f05e64 --- /dev/null +++ b/npm_and_yarn/spec/fixtures/projects/npm8/private_registry_ghpr_and_npm/package.json @@ -0,0 +1,23 @@ +{ + "name": "dependabot-consume-private-registries", + "version": "1.0.0", + "description": "Used by [#dependabot-updates-team](https://app.slack.com/client/T0CA8C346/C01BKB7EVQX) to test [support for private registries](https://github.com/github/dsp/issues/167).", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/dsp-testing/dependabot-consume-private-registries.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/dsp-testing/dependabot-consume-private-registries/issues" + }, + "homepage": "https://github.com/dsp-testing/dependabot-consume-private-registries#readme", + "dependencies": { + "@dsp-testing/inner-source-top-secret-npm-2": "1.0.3", + "lodash": "^4.17.20" + } +} diff --git a/npm_and_yarn/spec/fixtures/projects/npm8/private_registry_ghpr_only/package-lock.json b/npm_and_yarn/spec/fixtures/projects/npm8/private_registry_ghpr_only/package-lock.json new file mode 100644 index 00000000000..038cad7e904 --- /dev/null +++ b/npm_and_yarn/spec/fixtures/projects/npm8/private_registry_ghpr_only/package-lock.json @@ -0,0 +1,29 @@ +{ + "name": "dependabot-consume-private-registries", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "dependabot-consume-private-registries", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@dsp-testing/inner-source-top-secret-npm-2": "1.0.3" + } + }, + "node_modules/@dsp-testing/inner-source-top-secret-npm-2": { + "version": "1.0.3", + "resolved": "https://npm.pkg.github.com/download/@dsp-testing/inner-source-top-secret-npm-2/1.0.3/0a19a66110450848d0a88b1be211cadae740a86f0c1c1658ed89c9f391b8f605", + "integrity": "sha512-5Kt5AHgt2qE9YFlRnqizh36k1lcuTdGQP3UsxJgxVUo1Uxh4Z7vDgr7wDBm2hp4PjZ6soE4zupSyfaCbYguQqg==", + "license": "ISC" + } + }, + "dependencies": { + "@dsp-testing/inner-source-top-secret-npm-2": { + "version": "1.0.3", + "resolved": "https://npm.pkg.github.com/download/@dsp-testing/inner-source-top-secret-npm-2/1.0.3/0a19a66110450848d0a88b1be211cadae740a86f0c1c1658ed89c9f391b8f605", + "integrity": "sha512-5Kt5AHgt2qE9YFlRnqizh36k1lcuTdGQP3UsxJgxVUo1Uxh4Z7vDgr7wDBm2hp4PjZ6soE4zupSyfaCbYguQqg==" + } + } +} diff --git a/npm_and_yarn/spec/fixtures/projects/npm8/private_registry_ghpr_only/package.json b/npm_and_yarn/spec/fixtures/projects/npm8/private_registry_ghpr_only/package.json new file mode 100644 index 00000000000..87158c4c38b --- /dev/null +++ b/npm_and_yarn/spec/fixtures/projects/npm8/private_registry_ghpr_only/package.json @@ -0,0 +1,22 @@ +{ + "name": "dependabot-consume-private-registries", + "version": "1.0.0", + "description": "Used by [#dependabot-updates-team](https://app.slack.com/client/T0CA8C346/C01BKB7EVQX) to test [support for private registries](https://github.com/github/dsp/issues/167).", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/dsp-testing/dependabot-consume-private-registries.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/dsp-testing/dependabot-consume-private-registries/issues" + }, + "homepage": "https://github.com/dsp-testing/dependabot-consume-private-registries#readme", + "dependencies": { + "@dsp-testing/inner-source-top-secret-npm-2": "1.0.3" + } +} diff --git a/npm_and_yarn/spec/fixtures/projects/npm8/private_registry_ghpr_with_ports/package-lock.json b/npm_and_yarn/spec/fixtures/projects/npm8/private_registry_ghpr_with_ports/package-lock.json new file mode 100644 index 00000000000..bb7952f28d4 --- /dev/null +++ b/npm_and_yarn/spec/fixtures/projects/npm8/private_registry_ghpr_with_ports/package-lock.json @@ -0,0 +1,40 @@ +{ + "name": "dependabot-consume-private-registries", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "dependabot-consume-private-registries", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@dsp-testing/inner-source-top-secret-npm-2": "1.0.3", + "lodash": "^4.17.20" + } + }, + "node_modules/@dsp-testing/inner-source-top-secret-npm-2": { + "version": "1.0.3", + "resolved": "https://npm.pkg.github.com:443/download/@dsp-testing/inner-source-top-secret-npm-2/1.0.3/0a19a66110450848d0a88b1be211cadae740a86f0c1c1658ed89c9f391b8f605", + "integrity": "sha512-5Kt5AHgt2qE9YFlRnqizh36k1lcuTdGQP3UsxJgxVUo1Uxh4Z7vDgr7wDBm2hp4PjZ6soE4zupSyfaCbYguQqg==", + "license": "ISC" + }, + "node_modules/lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + } + }, + "dependencies": { + "@dsp-testing/inner-source-top-secret-npm-2": { + "version": "1.0.3", + "resolved": "https://npm.pkg.github.com:443/download/@dsp-testing/inner-source-top-secret-npm-2/1.0.3/0a19a66110450848d0a88b1be211cadae740a86f0c1c1658ed89c9f391b8f605", + "integrity": "sha512-5Kt5AHgt2qE9YFlRnqizh36k1lcuTdGQP3UsxJgxVUo1Uxh4Z7vDgr7wDBm2hp4PjZ6soE4zupSyfaCbYguQqg==" + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + } + } +} diff --git a/npm_and_yarn/spec/fixtures/projects/npm8/private_registry_ghpr_with_ports/package.json b/npm_and_yarn/spec/fixtures/projects/npm8/private_registry_ghpr_with_ports/package.json new file mode 100644 index 00000000000..0a309f05e64 --- /dev/null +++ b/npm_and_yarn/spec/fixtures/projects/npm8/private_registry_ghpr_with_ports/package.json @@ -0,0 +1,23 @@ +{ + "name": "dependabot-consume-private-registries", + "version": "1.0.0", + "description": "Used by [#dependabot-updates-team](https://app.slack.com/client/T0CA8C346/C01BKB7EVQX) to test [support for private registries](https://github.com/github/dsp/issues/167).", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/dsp-testing/dependabot-consume-private-registries.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/dsp-testing/dependabot-consume-private-registries/issues" + }, + "homepage": "https://github.com/dsp-testing/dependabot-consume-private-registries#readme", + "dependencies": { + "@dsp-testing/inner-source-top-secret-npm-2": "1.0.3", + "lodash": "^4.17.20" + } +}