Skip to content

Singles: Working level step toward unaffected_versions field automation in support of #649 #670

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
132 changes: 130 additions & 2 deletions lib/github_advisory_sync.rb
Original file line number Diff line number Diff line change
Expand Up @@ -366,17 +366,145 @@ def patched_versions_for(package)
return patched_versions
end

def first_unaffected_versions_for(package)
first_unaffected_versions = []

vulnerabilities.each do |v|
if v['package']['name'] == package.name &&
v['vulnerableVersionRange']
first_unaffected_versions << v['vulnerableVersionRange']
end
end

first_unaffected_versions.sort
end

def set_fpv_vars(first_patched_versions)
if first_patched_versions.count > 0
if first_patched_versions.count == 1
fpv_operator = first_patched_versions.last[0,1]
fpv_value = first_patched_versions.last[3..first_patched_versions.last.length]
#puts "FPV=#{first_patched_versions}; COUNT=[#{first_patched_versions.length}]"
#puts "FPV[1]=[#{fpv_operator}]; FPV[2]=[#{fpv_value}]"
else # > 1
puts "TODO: #Use Case 4: Multiple patched_verions ranges: TBD"
fpv_operator = "MULTIPLE"
fpv_value = "MULTIPLE"
end
else # == 0
#fpv_operator = "EMPTY"
#fpv_value = "EMPTY"
end

return fpv_operator, fpv_value
end

def unaffected_versions_for(package)
# The unaffected_versions field is similarly not directly available.
# This optional field must be inferred from the vulnerableVersionRange.
#
# EXAMPLE YAML OUTPUT:
#vulnerabilities:
#- package:
# name: redcloth
# ecosystem: RUBYGEMS
# vulnerableVersionRange: "< 4.3.0"
# firstPatchedVersion:
# identifier: 4.3.0

# Need this code to compare vulnerableVersionRange and firstPatchedVersion[identifier]
first_patched_versions = patched_versions_for(package)

fpv_operator, fpv_value = set_fpv_vars(first_patched_versions)

first_unaffected_versions = first_unaffected_versions_for(package)
unaffected_versions = []

if !first_unaffected_versions.empty?
unaff_vers_range =
first_unaffected_versions.last[2..first_unaffected_versions.last.length]

case first_unaffected_versions.last[0,1]
when "<"
if first_unaffected_versions.last[0,2] == "<="
#Use Case 3: Example: TBD
# vulnerableVersionRange: "<= 1.3.1"
# firstPatchedVersion:
# identifier: 1.4.0
#THEREFORE: unaffected_versions: "> 1.3.1, < 4.1.0"
unaff_vers_range =
first_unaffected_versions.last[3..first_unaffected_versions.last.length]
puts "uv3: [<=]: > #{unaff_vers_range}, < #{fpv_value}"
unaffected_versions << "> #{unaff_vers_range}, < #{fpv_value}"
else
if unaff_vers_range == fpv_value
#Use Case 1: gems/nokogiri/GHSA-fq42-c5rg-92c2.yml
# vulnerableVersionRange: "< 1.13.2"
# firstPatchedVersion:
# identifier: 1.13.2
# THEREFORE: Do nothing.
puts "uc1A: [< && fvr == fpv_value], so do nothing"
else # < && !=
puts "uc1B: [< && fvr != fpv_value], so UNKNOWN"
end
end
when "="
if unaff_vers_range < fpv_value
#Use Case 2: Example: gems/spree_auth_devise/GHSA-8xfaw-5q82-3652.yml
# vulnerableVersionRange:"= 4.1.0"
# firstPatchedVersion:
# identifier: 4.1.1
#THEREFORE: unaffected_versions: "< 4.1.0"
puts "uc4A: [=, uvr < fpv_v]: < #{unaff_vers_range}"
unaffected_versions << "< #{unaff_vers_range}"
else # = & !=
puts "uc4B: [=, uvr >= fpv_v]: UNKNOWN"
unaffected_verions << "[=, uvr >= fpv_v]: UNKNOWN"
end
when ">"
if first_unaffected_versions.last[0,2] == ">="
unaff_vers_range =
first_unaffected_versions.last[4..first_unaffected_versions.last.length]
puts "[>=]: #{unaff_vers_range}"
unaffected_versions << "[>=]: #{unaff_vers_range}"
else
puts "[>, !>=]: #{unaff_vers_range}"
unaffected_versions << "[>, !>=]: #{unaff_vers_range}"
end
when "!"
puts "[!]: #{unaff_vers_range}"
unaffected_versions << "[! OPERATOR]: #{unaff_vers_range}"
when "~"
puts "[~]: #{unaff_vers_range}"
unaffected_versions << "[~ OPERATOR]: #{unaff_vers_range}"
else
puts "[UNK OPERATOR]: #{unaff_vers_range}"
unaffected_versions << "[UNK OPERATOR]: #{unaff_vers_range}"
end
end

return unaffected_versions
end

def create(package)
filename_to_write = package.filename

new_data = package.merge_data(
"cvss_v3" => ("<FILL IN IF AVAILABLE>" unless cvss),
"cvss_v4" => "<FILL IN IF AVAILABLE>",
"unaffected_versions" => ["<OPTIONAL: FILL IN SEE BELOW>"]
"cvss_v4" => "<FILL IN IF AVAILABLE>"
)

unaffected_versions = unaffected_versions_for(package)

patched_versions = patched_versions_for(package)

if !unaffected_versions.empty?
new_data['unaffected_versions'] = unaffected_versions if !patched_versions.empty?
else
# NOTE: Do not add "unaffected_versions:" field if empty.
puts "DEBUG: unaffected_versions: All Affected"
end

if !patched_versions.empty?
new_data['patched_versions'] = patched_versions
else
Expand Down