Last active
April 8, 2021 18:08
-
-
Save BobbyMcWho/3ce09bde5abb674e61092efbe7390ffb to your computer and use it in GitHub Desktop.
Some code to create `Dependabot::SecurityAdvisory`s for use in dependabot-core scripts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# See: https://github.com/dependabot/dependabot-script/blob/master/update-script.rb | |
# Other update script logic | |
vulnerabilities = VulnerabilityFetcher.new(dependency_names, package_manager).fetch_advisories | |
# Note you may not just want top level depending on your use case | |
dependencies.select(&:top_level?).each do |dep| | |
security_vulnerabilities = [] | |
if vulnerabilities.any? | |
security_vulnerabilities = vulnerabilities[dep.name.to_sym].map do |vuln| | |
vulnerable_versions = vuln[:vulnerable_versions].map { |v| requirement_class.new(v) } | |
safe_versions = vuln[:patched_versions].map { |v| requirement_class.new(v) } | |
Dependabot::SecurityAdvisory.new( | |
dependency_name: dep.name, | |
package_manager: package_manager, | |
cve_id: vuln[:cve_id], | |
url: vuln[:url], | |
summary: vuln[:summary], | |
vulnerable_versions: vulnerable_versions, | |
safe_versions: safe_versions | |
) | |
end | |
end | |
######################################### | |
# Get update details for the dependency # | |
######################################### | |
checker = Dependabot::UpdateCheckers.for_package_manager(package_manager).new( | |
dependency: dep, | |
dependency_files: files, | |
credentials: credentials, | |
security_advisories: security_vulnerabilities | |
) | |
# Other update script PR logic and such |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'octokit' | |
class VulnerabilityFetcher | |
attr_reader :packages, :package_manager | |
def initialize(packages, package_manager) | |
@packages = packages | |
@package_manager = package_manager | |
end | |
def fetch_advisories | |
# Github API does not support hex vulnerabilities | |
if package_manager == 'hex' | |
fetch_elixir_advisories | |
# Github's vulnerability API is sorely slow to update | |
# compared to bundler-audit|ruby-advisory-db | |
elsif package_manager == 'bundler' | |
fetch_ruby_advisories | |
else | |
fetch_github_advisories | |
end | |
end | |
private | |
def fetch_ruby_advisories | |
results = {} | |
packages.each do |package| | |
results[package] = begin | |
client.content('rubysec/ruby-advisory-db', path: "gems/#{package}").map do |file| | |
encoded_content = client.content('rubysec/ruby-advisory-db', path: file.path).content | |
decoded_content = Base64.decode64(encoded_content) | |
YAML.load(decoded_content) | |
end | |
rescue Octokit::NotFound | |
[] | |
end | |
end | |
format_ruby_dependencies(results) | |
end | |
def fetch_elixir_advisories | |
results = {} | |
packages.each do |package| | |
results[package] = begin | |
client.content('dependabot/elixir-security-advisories', path: "packages/#{package}").map do |file| | |
encoded_content = client.content('dependabot/elixir-security-advisories', path: file.path).content | |
decoded_content = Base64.decode64(encoded_content) | |
YAML.load(decoded_content) | |
end | |
rescue Octokit::NotFound | |
[] | |
end | |
end | |
format_elixir_dependencies(results) | |
end | |
def format_elixir_dependencies(raw_response) | |
advisories = {} | |
raw_response.each do |key, val| | |
advisories[key.to_sym] = val.map do |v| | |
{ | |
patched_versions: v["patched_versions"], | |
vulnerable_versions: [], | |
cve_id: v["cve"], | |
url: v["link"], | |
summary: v["title"] | |
} | |
end | |
end | |
advisories | |
end | |
def format_ruby_dependencies(raw_response) | |
advisories = {} | |
raw_response.each do |key, val| | |
advisories[key.to_sym] = val.map do |v| | |
{ | |
patched_versions: v["patched_versions"], | |
vulnerable_versions: [], | |
cve_id: v["cve"], | |
url: v["url"], | |
summary: v["title"] | |
} | |
end | |
end | |
advisories | |
end | |
def fetch_github_advisories | |
response = client.post('/graphql', { query: query(packages, package_manager) }.to_json) | |
format_github_advisories(response) | |
end | |
def format_github_advisories(raw_response) | |
advisories = {} | |
raw_response[:data]&.each do |key, val| | |
advisories[key] = val[:vulnerabilities].map do |v| | |
{ | |
patched_versions: [], | |
vulnerable_versions: [v.vulnerable_version_range], | |
cve_id: v.advisory.identifiers.find { |a| a.type == 'CVE' }.id, | |
url: v.advisory.references.first[:url], | |
summary: v.advisory.summary | |
} | |
end | |
end | |
advisories | |
end | |
def client | |
@client ||= Octokit::Client.new(access_token: ENV["PUBLIC_GITHUB_TOKEN"]) | |
end | |
def query(packages, package_manager) | |
query = "query {\n" | |
packages.each do |package| | |
query += <<~PACKAGE_QUERY | |
#{package}: securityVulnerabilities( | |
ecosystem: #{ecosystem_for(package_manager)}, | |
package: "#{package}", | |
first: 100 | |
) { | |
vulnerabilities: nodes { | |
vulnerable_version_range: vulnerableVersionRange | |
advisory { | |
identifiers { | |
type: type | |
id: value | |
} | |
summary | |
references { | |
url | |
} | |
} | |
} | |
}\n | |
PACKAGE_QUERY | |
end | |
query += "}" | |
end | |
ECOSYSTEMS = { | |
"bundler" => "RUBYGEMS", | |
"npm_and_yarn" => "NPM", | |
"pip" => "PIP", | |
"composer" => "COMPOSER" | |
}.freeze | |
def ecosystem_for(package_manager) | |
ECOSYSTEMS[package_manager] | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment