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 13ed0a3e3f4..b2f3907879a 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 @@ -96,13 +96,19 @@ def requirements_update_strategy end def conflicting_dependencies - ConflictingDependencyResolver.new( + conflicts = ConflictingDependencyResolver.new( dependency_files: dependency_files, credentials: credentials ).conflicting_dependencies( dependency: dependency, target_version: lowest_security_fix_version ) + + vulnerable = vulnerability_audit.select do |v| + !v["fix_available"] && v["explanation"] + end + + conflicts + vulnerable end private 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 2c722842497..1fb5f6b8702 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 @@ -21,6 +21,7 @@ def initialize(dependency_files:, credentials:, allow_removal: false) @allow_removal = allow_removal end + # rubocop:disable Metrics/MethodLength # Finds any dependencies in the `package-lock.json` or `npm-shrinkwrap.json` that have # a subdependency on the given dependency that is locked to a vuln version range. # @@ -41,6 +42,7 @@ def initialize(dependency_files:, credentials:, allow_removal: false) # dependency on the blocking dependency # * :top_level_ancestors [Array] the names of all top-level dependencies with a transitive # dependency on the dependency + # * :explanation [String] an explanation for why the project failed the vulnerability auditor run def audit(dependency:, security_advisories:) fix_unavailable = { "dependency_name" => dependency.name, @@ -74,7 +76,12 @@ def audit(dependency:, security_advisories:) function: "npm:vulnerabilityAuditor", args: [Dir.pwd, vuln_versions] ) - return fix_unavailable unless viable_audit_result?(audit_result, security_advisories) + + validation_result = validate_audit_result(audit_result, security_advisories) + unless viable_audit_result?(validation_result) + fix_unavailable["explanation"] = explain_fix_unavailable(validation_result, dependency) + return fix_unavailable + end audit_result end @@ -82,13 +89,23 @@ def audit(dependency:, security_advisories:) log_helper_subprocess_failure(dependency, e) fix_unavailable end + # rubocop:enable Metrics/MethodLength private attr_reader :dependency_files, :credentials - def viable_audit_result?(audit_result, security_advisories) - validation_result = validate_audit_result(audit_result, security_advisories) + def explain_fix_unavailable(validation_result, dependency) + case validation_result + when :fix_unavailable, :dependency_still_vulnerable, :downgrades_dependencies + "No patched version available for #{dependency.name}" + when :vulnerable_dependency_removed + "#{dependency.name} was removed in the update. Dependabot is not able to " \ + "deal with this yet, but you can still upgrade manually." + end + end + + def viable_audit_result?(validation_result) return true if validation_result == :viable Dependabot.logger.info("VulnerabilityAuditor: audit result not viable: #{validation_result}") 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 83de366e4c0..733565d249d 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 @@ -110,7 +110,11 @@ 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( + "fix_available" => false, + "explanation" => "#{dependency.name} was removed in the update. "\ + "Dependabot is not able to deal with this yet, but you can still upgrade manually." + ) end end end @@ -137,7 +141,10 @@ expect(Dependabot.logger).to receive(:info).with(/audit result not viable: dependency_still_vulnerable/i) expect(subject.audit(dependency: dependency, security_advisories: security_advisories)). - to include("fix_available" => false) + to include( + "fix_available" => false, + "explanation" => "No patched version available for #{dependency.name}" + ) end end @@ -172,7 +179,10 @@ expect(Dependabot.logger).to receive(:info).with(/audit result not viable: downgrades_dependencies/i) expect(subject.audit(dependency: dependency, security_advisories: security_advisories)). - to include("fix_available" => false) + to include( + "fix_available" => false, + "explanation" => "No patched version available for #{dependency.name}" + ) end end