diff --git a/npm_and_yarn/lib/dependabot/npm_and_yarn.rb b/npm_and_yarn/lib/dependabot/npm_and_yarn.rb index 3e36c2501e9..b500637009c 100644 --- a/npm_and_yarn/lib/dependabot/npm_and_yarn.rb +++ b/npm_and_yarn/lib/dependabot/npm_and_yarn.rb @@ -61,6 +61,16 @@ module NpmAndYarn SOCKET_HANG_UP = /(?.*?): socket hang up/ + # Misc errors + EEXIST = /EEXIST: file already exists, mkdir '(?.*)'/ + + # registry access errors + REQUEST_ERROR_E403 = /Request "(?.*)" returned a 403/ # Forbidden access to the URL. + AUTH_REQUIRED_ERROR = /(?.*): authentication required/ # Authentication is required for the URL. + PERMISSION_DENIED = /(?.*): Permission denied/ # Lack of permission to access the URL. + BAD_REQUEST = /(?.*): bad_request/ # Inconsistent request while accessing resource. + INTERNAL_SERVER_ERROR = /Request failed "500 Internal Server Error"/ # Server error response by remote registry. + # Used to identify git unreachable error UNREACHABLE_GIT_CHECK_REGEX = /ls-remote --tags --heads (?.*)/ @@ -79,6 +89,8 @@ module NpmAndYarn PACKAGE_NOT_FOUND_PACKAGE_NAME_CAPTURE = "package_req" PACKAGE_NOT_FOUND_PACKAGE_NAME_CAPTURE_SPLIT_REGEX = /(?<=\w)\@/ + YARN_PACKAGE_NOT_FOUND_CODE = /npm package "(?.*)" does not exist under owner "(?.*)"/ + YN0035 = T.let({ PACKAGE_NOT_FOUND: %r{(?@[\w-]+\/[\w-]+@\S+): Package not found}, FAILED_TO_RETRIEVE: %r{(?@[\w-]+\/[\w-]+@\S+): The remote server failed to provide the requested resource} # rubocop:disable Layout/LineLength @@ -97,6 +109,9 @@ module NpmAndYarn DEPENDENCY_NO_VERSION_FOUND = "Couldn't find any versions" + # Manifest not found + MANIFEST_NOT_FOUND = /Cannot read properties of undefined \(reading '(?.*)'\)/ + # Used to identify error if node_modules state file not resolved NODE_MODULES_STATE_FILE_NOT_FOUND = "Couldn't find the node_modules state file" @@ -512,8 +527,45 @@ def self.sanitize_resolvability_message(error_message, dependencies, yarn_lock) }, in_usage: false, matchfn: nil - } + }, + { + patterns: [YARN_PACKAGE_NOT_FOUND_CODE], + handler: lambda { |message, _error, _params| + msg = message.match(YARN_PACKAGE_NOT_FOUND_CODE) + Dependabot::DependencyFileNotResolvable.new(msg) + }, + in_usage: false, + matchfn: nil + }, + { + patterns: [REQUEST_ERROR_E403, AUTH_REQUIRED_ERROR, PERMISSION_DENIED, BAD_REQUEST], + handler: lambda { |message, _error, _params| + dependency_url = T.must(URI.decode_www_form_component(message).split("https://").last).split("/").first + + Dependabot::PrivateSourceAuthenticationFailure.new(dependency_url) + }, + in_usage: false, + matchfn: nil + }, + { + patterns: [MANIFEST_NOT_FOUND], + handler: lambda { |message, _error, _params| + msg = message.match(MANIFEST_NOT_FOUND) + Dependabot::DependencyFileNotResolvable.new(msg) + }, + in_usage: false, + matchfn: nil + }, + { + patterns: [INTERNAL_SERVER_ERROR], + handler: lambda { |message, _error, _params| + msg = message.match(INTERNAL_SERVER_ERROR) + Dependabot::DependencyFileNotResolvable.new(msg) + }, + in_usage: false, + matchfn: nil + } ].freeze, T::Array[{ patterns: T::Array[T.any(String, Regexp)], handler: ErrorHandler, diff --git a/npm_and_yarn/lib/dependabot/npm_and_yarn/file_updater/npm_lockfile_updater.rb b/npm_and_yarn/lib/dependabot/npm_and_yarn/file_updater/npm_lockfile_updater.rb index 5c8eb242971..0bce33ba621 100644 --- a/npm_and_yarn/lib/dependabot/npm_and_yarn/file_updater/npm_lockfile_updater.rb +++ b/npm_and_yarn/lib/dependabot/npm_and_yarn/file_updater/npm_lockfile_updater.rb @@ -95,7 +95,7 @@ def updated_lockfile_reponse(response) NPM_PACKAGE_NOT_FOUND_CODES = T.let([ /Couldn't find package "(?.*)" on the "(?.*)" registry./, - /Couldn't find package "(?.*)" "\required by "(?.*)" on the "(?.*)" registry./ + /Couldn't find package "(?.*)" required by "(?.*)" on the "(?.*)" registry./ ].freeze, T::Array[Regexp]) # TODO: look into fixing this in npm, seems like a bug in the git diff --git a/npm_and_yarn/spec/dependabot/npm_and_yarn/yarn_error_handler_spec.rb b/npm_and_yarn/spec/dependabot/npm_and_yarn/yarn_error_handler_spec.rb index 3a7ddf64866..64a15445390 100644 --- a/npm_and_yarn/spec/dependabot/npm_and_yarn/yarn_error_handler_spec.rb +++ b/npm_and_yarn/spec/dependabot/npm_and_yarn/yarn_error_handler_spec.rb @@ -641,6 +641,106 @@ end end + context "when the error message contains undefined manifest error" do + let(:error_message) do + "Cannot read properties of undefined (reading 'manifest')" + end + + it "raises the corresponding error class with the correct message" do + expect { error_handler.handle_group_patterns(error, usage_error_message, { yarn_lock: yarn_lock }) } + .to raise_error(Dependabot::DependencyFileNotResolvable, + "Cannot read properties of undefined (reading 'manifest')") + end + end + + context "when the error message contains 403 error" do + let(:error_message) do + "https://artifactory.wikia-inc.com/artifactory/api/npm/wikia-npm/@fandom-frontend%2fdesign-system: " \ + "Request \"https://artifactory.wikia-inc.com/artifactory/api/npm/wikia-npm/@fandom-frontend%2fdes" \ + "ign-system\" returned a 403" + end + + it "raises the corresponding error class with the correct message" do + expect { error_handler.handle_group_patterns(error, usage_error_message, { yarn_lock: yarn_lock }) } + .to raise_error(Dependabot::PrivateSourceAuthenticationFailure, + "The following source could not be reached" \ + " as it requires authentication " \ + "(and any provided details were invalid or lacked " \ + "the required permissions): artifactory.wikia-inc.com") + end + end + + context "when the error message contains authentication required error" do + let(:error_message) do + "https://npm.shopify.io/node/@shopify%2fpolaris-icons: authentication required" + end + + it "raises the corresponding error class with the correct message" do + expect { error_handler.handle_group_patterns(error, usage_error_message, { yarn_lock: yarn_lock }) } + .to raise_error(Dependabot::PrivateSourceAuthenticationFailure, + "The following source could not be reached" \ + " as it requires authentication " \ + "(and any provided details were invalid or lacked " \ + "the required permissions): npm.shopify.io") + end + end + + context "when the error message contains Permission denied error" do + let(:error_message) do + "https://npm.pkg.github.com/breakthroughbehavioralinc/webpack: Permission denied" + end + + it "raises the corresponding error class with the correct message" do + expect { error_handler.handle_group_patterns(error, usage_error_message, { yarn_lock: yarn_lock }) } + .to raise_error(Dependabot::PrivateSourceAuthenticationFailure, + "The following source could not be reached" \ + " as it requires authentication " \ + "(and any provided details were invalid or lacked " \ + "the required permissions): npm.pkg.github.com") + end + end + + context "when the error message contains Permission denied error" do + let(:error_message) do + "https://npm-proxy.fury.io/rps/webpack: bad_request" + end + + it "raises the corresponding error class with the correct message" do + expect { error_handler.handle_group_patterns(error, usage_error_message, { yarn_lock: yarn_lock }) } + .to raise_error(Dependabot::PrivateSourceAuthenticationFailure, + "The following source could not be reached" \ + " as it requires authentication " \ + "(and any provided details were invalid or lacked " \ + "the required permissions): npm-proxy.fury.io") + end + end + + context "when the error message contains Internal Server Error error" do + let(:error_message) do + "ResponseError: Request failed \"500 Internal Server Error\"" \ + "at params.callback [as _callback] (/opt/npm_and_yarn/node_modules/" + end + + it "raises the corresponding error class with the correct message" do + expect { error_handler.handle_group_patterns(error, usage_error_message, { yarn_lock: yarn_lock }) } + .to raise_error(Dependabot::DependencyFileNotResolvable, + "Request failed \"500 Internal Server Error\"") + end + end + + context "when the error message contains no package found error" do + let(:error_message) do + "https://npm.pkg.github.com/@graphql-codegen%2ftypescript-react-apollo:" \ + " npm package \"typescript-react-apollo\" does not exist under owner \"graphql-codegen\"" + end + + it "raises the corresponding error class with the correct message" do + expect { error_handler.handle_group_patterns(error, usage_error_message, { yarn_lock: yarn_lock }) } + .to raise_error(Dependabot::DependencyFileNotResolvable, + "npm package \"typescript-react-apollo\" does not exist under owner \"graphql-codegen\"") + end + end + context "when the error message contains YARNRC_ENV_NOT_FOUND" do let(:error_message) do "Usage Error: Environment variable not found (GITHUB_TOKEN) in /home/dependabot/dependabot-" \