Skip to content

Commit

Permalink
Merge branch 'main' into jamiemagee/sentry-ruby
Browse files Browse the repository at this point in the history
  • Loading branch information
JamieMagee authored Jan 19, 2024
2 parents 196c312 + a1b5e0c commit b1b9dd1
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 18 deletions.
64 changes: 48 additions & 16 deletions common/lib/dependabot/security_advisory.rb
Original file line number Diff line number Diff line change
@@ -1,31 +1,51 @@
# typed: true
# typed: strong
# frozen_string_literal: true

require "sorbet-runtime"
require "dependabot/version"

module Dependabot
class SecurityAdvisory
attr_reader :dependency_name, :package_manager,
:vulnerable_versions, :safe_versions,
:vulnerable_version_strings
extend T::Sig

sig { returns(String) }
attr_reader :dependency_name

sig { returns(String) }
attr_reader :package_manager

sig { returns(T::Array[Dependabot::Requirement]) }
attr_reader :vulnerable_versions

sig { returns(T::Array[Dependabot::Requirement]) }
attr_reader :safe_versions

sig { returns(T::Array[T.any(String, Dependabot::Requirement)]) }
attr_reader :vulnerable_version_strings

sig do
params(
dependency_name: String,
package_manager: String,
vulnerable_versions: T.nilable(T::Array[Dependabot::Requirement]),
safe_versions: T.nilable(T::Array[T.any(String, Dependabot::Requirement)])
)
.void
end
def initialize(dependency_name:, package_manager:,
vulnerable_versions: [], safe_versions: [])
@dependency_name = dependency_name
@package_manager = package_manager
@vulnerable_version_strings = vulnerable_versions || []
@vulnerable_versions = []
@safe_versions = safe_versions || []
@vulnerable_version_strings = T.let(vulnerable_versions || [], T::Array[T.any(String, Dependabot::Requirement)])
@vulnerable_versions = T.let([], T::Array[Dependabot::Requirement])
@safe_versions = T.let([], T::Array[Dependabot::Requirement])

convert_string_version_requirements
convert_string_version_requirements(vulnerable_version_strings, safe_versions || [])
check_version_requirements
end

sig { params(version: Gem::Version).returns(T::Boolean) }
def vulnerable?(version)
unless version.is_a?(version_class) || version.instance_of?(Gem::Version)
raise ArgumentError, "must be a #{version_class}"
end

in_safe_range = safe_versions
.any? { |r| r.satisfied_by?(version) }

Expand All @@ -50,9 +70,10 @@ def vulnerable?(version)
#
# @param dependency [Dependabot::Dependency] Updated dependency
# @return [Boolean]
sig { params(dependency: Dependabot::Dependency).returns(T::Boolean) }
def fixed_by?(dependency)
# Handle case mismatch between the security advisory and parsed name
return false unless dependency_name.casecmp(dependency.name).zero?
return false unless dependency_name.casecmp(dependency.name)&.zero?
return false unless package_manager == dependency.package_manager
# TODO: Support no previous version to the same level as dependency graph
# and security alerts. We currently ignore dependency updates without a
Expand All @@ -61,19 +82,20 @@ def fixed_by?(dependency)
return false unless version_class.correct?(dependency.previous_version)

# Ignore deps that weren't previously vulnerable
return false unless affects_version?(dependency.previous_version)
return false unless affects_version?(T.must(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)
!affects_version?(T.must(dependency.version))
end

# Check if the version is affected by the advisory
#
# @param version [Dependabot::<Package Manager>::Version] version class
# @return [Boolean]
sig { params(version: T.any(String, Gem::Version)).returns(T::Boolean) }
def affects_version?(version)
return false unless version_class.correct?(version)
return false unless [*safe_versions, *vulnerable_versions].any?
Expand All @@ -96,7 +118,14 @@ def affects_version?(version)

private

def convert_string_version_requirements
sig do
params(
vulnerable_version_strings: T::Array[T.any(String, Dependabot::Requirement)],
safe_versions: T::Array[T.any(String, Dependabot::Requirement)]
)
.void
end
def convert_string_version_requirements(vulnerable_version_strings, safe_versions)
@vulnerable_versions = vulnerable_version_strings.flat_map do |vuln_str|
next vuln_str unless vuln_str.is_a?(String)

Expand All @@ -110,6 +139,7 @@ def convert_string_version_requirements
end
end

sig { void }
def check_version_requirements
unless vulnerable_versions.is_a?(Array) &&
vulnerable_versions.all? { |i| requirement_class <= i.class }
Expand All @@ -124,10 +154,12 @@ def check_version_requirements
end
end

sig { returns(T.class_of(Gem::Version)) }
def version_class
Utils.version_class_for_package_manager(package_manager)
end

sig { returns(T.class_of(Dependabot::Requirement)) }
def requirement_class
Utils.requirement_class_for_package_manager(package_manager)
end
Expand Down
4 changes: 2 additions & 2 deletions common/lib/dependabot/update_checkers/version_filters.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ module VersionFilters

sig do
params(
versions_array: T::Array[T.any(Gem::Version, T::Hash[Symbol, String])],
versions_array: T::Array[T.any(Gem::Version, T::Hash[Symbol, Gem::Version])],
security_advisories: T::Array[SecurityAdvisory]
)
.returns(T::Array[T.any(Gem::Version, T::Hash[Symbol, String])])
.returns(T::Array[T.any(Gem::Version, T::Hash[Symbol, Gem::Version])])
end
def self.filter_vulnerable_versions(versions_array, security_advisories)
versions_array.reject do |v|
Expand Down

0 comments on commit b1b9dd1

Please sign in to comment.