Skip to content

Instantly share code, notes, and snippets.

@BobbyMcWho
Last active April 8, 2021 18:08
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save BobbyMcWho/3ce09bde5abb674e61092efbe7390ffb to your computer and use it in GitHub Desktop.
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
# 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
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