rubocop commit
This commit is contained in:
parent
c4240a4b2e
commit
2b28810cf2
96 changed files with 845 additions and 664 deletions
27
Gemfile
27
Gemfile
|
@ -1,28 +1,31 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
source 'https://rubygems.org'
|
source 'https://rubygems.org'
|
||||||
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
|
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
|
||||||
|
|
||||||
ruby '2.7.0'
|
ruby '2.7.0'
|
||||||
|
|
||||||
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
|
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
|
||||||
gem 'rails', '~> 7.0.0'
|
|
||||||
gem 'actionpack'
|
gem 'actionpack'
|
||||||
gem 'sass-rails'
|
gem 'faktory_worker_ruby'
|
||||||
gem 'railties'
|
|
||||||
gem 'rest-client'
|
|
||||||
gem 'twitter'
|
|
||||||
gem 'tweetkit', github: 'julianfssen/tweetkit' # for twitter v2 api support
|
|
||||||
gem 'nokogiri'
|
|
||||||
gem 'graphql'
|
gem 'graphql'
|
||||||
gem 'graphql-client'
|
gem 'graphql-client'
|
||||||
|
gem 'nokogiri'
|
||||||
|
gem 'rails', '~> 7.0.0'
|
||||||
|
gem 'railties'
|
||||||
|
gem 'rest-client'
|
||||||
gem 'retryable'
|
gem 'retryable'
|
||||||
gem 'rubocop'
|
gem 'rubocop'
|
||||||
|
gem 'rubocop-graphql'
|
||||||
gem 'rubocop-rails'
|
gem 'rubocop-rails'
|
||||||
gem 'faktory_worker_ruby'
|
gem 'sass-rails'
|
||||||
|
gem 'tweetkit', github: 'julianfssen/tweetkit' # for twitter v2 api support
|
||||||
|
gem 'twitter'
|
||||||
|
|
||||||
# Use postgres as the database for Active Record
|
# Use postgres as the database for Active Record
|
||||||
gem 'pg'
|
|
||||||
gem 'bulk_insert'
|
gem 'bulk_insert'
|
||||||
gem 'git'
|
gem 'git'
|
||||||
|
gem 'pg'
|
||||||
# Use Puma as the app server
|
# Use Puma as the app server
|
||||||
gem 'puma', '~> 3.11'
|
gem 'puma', '~> 3.11'
|
||||||
# Use Uglifier as compressor for JavaScript assets
|
# Use Uglifier as compressor for JavaScript assets
|
||||||
|
@ -52,8 +55,8 @@ gem 'bootsnap', '>= 1.1.0', require: false
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
|
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
|
||||||
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
|
|
||||||
gem 'awesome_print' # pretty print ruby objects
|
gem 'awesome_print' # pretty print ruby objects
|
||||||
|
gem 'byebug', platforms: %i[mri mingw x64_mingw]
|
||||||
gem 'pry' # Console with powerful introspection capabilities
|
gem 'pry' # Console with powerful introspection capabilities
|
||||||
gem 'pry-byebug' # Integrates pry with byebug
|
gem 'pry-byebug' # Integrates pry with byebug
|
||||||
gem 'pry-doc' # Provide MRI Core documentation
|
gem 'pry-doc' # Provide MRI Core documentation
|
||||||
|
@ -64,8 +67,8 @@ end
|
||||||
|
|
||||||
group :development do
|
group :development do
|
||||||
# Access an interactive console on exception pages or by calling 'console' anywhere in the code.
|
# Access an interactive console on exception pages or by calling 'console' anywhere in the code.
|
||||||
gem 'web-console', '>= 3.3.0'
|
|
||||||
gem 'listen', '>= 3.0.5', '< 3.2'
|
gem 'listen', '>= 3.0.5', '< 3.2'
|
||||||
|
gem 'web-console', '>= 3.3.0'
|
||||||
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
|
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
|
||||||
gem 'spring'
|
gem 'spring'
|
||||||
gem 'spring-watcher-listen', '~> 2.0.0'
|
gem 'spring-watcher-listen', '~> 2.0.0'
|
||||||
|
@ -80,4 +83,4 @@ group :test do
|
||||||
end
|
end
|
||||||
|
|
||||||
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
|
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
|
||||||
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
|
gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby]
|
||||||
|
|
|
@ -296,6 +296,8 @@ GEM
|
||||||
unicode-display_width (>= 1.4.0, < 3.0)
|
unicode-display_width (>= 1.4.0, < 3.0)
|
||||||
rubocop-ast (1.17.0)
|
rubocop-ast (1.17.0)
|
||||||
parser (>= 3.1.1.0)
|
parser (>= 3.1.1.0)
|
||||||
|
rubocop-graphql (0.14.2)
|
||||||
|
rubocop (>= 0.87, < 2)
|
||||||
rubocop-rails (2.14.2)
|
rubocop-rails (2.14.2)
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
rack (>= 1.1)
|
rack (>= 1.1)
|
||||||
|
@ -404,6 +406,7 @@ DEPENDENCIES
|
||||||
rest-client
|
rest-client
|
||||||
retryable
|
retryable
|
||||||
rubocop
|
rubocop
|
||||||
|
rubocop-graphql
|
||||||
rubocop-rails
|
rubocop-rails
|
||||||
sass-rails
|
sass-rails
|
||||||
selenium-webdriver
|
selenium-webdriver
|
||||||
|
|
2
Rakefile
2
Rakefile
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
||||||
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module ApplicationCable
|
module ApplicationCable
|
||||||
class Channel < ActionCable::Channel::Base
|
class Channel < ActionCable::Channel::Base
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module ApplicationCable
|
module ApplicationCable
|
||||||
class Connection < ActionCable::Connection::Base
|
class Connection < ActionCable::Connection::Base
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class ApplicationController < ActionController::Base
|
class ApplicationController < ActionController::Base
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class CnasController < ApplicationController
|
class CnasController < ApplicationController
|
||||||
def index
|
def index
|
||||||
@cnas = Cna.all
|
@cnas = Cna.all
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class CpesController < ApplicationController
|
class CpesController < ApplicationController
|
||||||
def index
|
def index
|
||||||
@cpes = Cpe.all
|
@cpes = Cpe.all
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class CvemonCvesController < ApplicationController
|
class CvemonCvesController < ApplicationController
|
||||||
def index
|
def index
|
||||||
@pocs = CvemonCve.all
|
@pocs = CvemonCve.all
|
||||||
|
@ -5,12 +7,12 @@ class CvemonCvesController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@poc = CvemonCve.find_by(:id => params[:id])
|
@poc = CvemonCve.find_by(id: params[:id])
|
||||||
render json: @poc.to_json
|
render json: @poc.to_json
|
||||||
end
|
end
|
||||||
|
|
||||||
def show_for_cve
|
def show_for_cve
|
||||||
@poc = CvemonCve.where(:cve_id => params[:cve_id])
|
@poc = CvemonCve.where(cve_id: params[:cve_id])
|
||||||
render json: @poc.to_json
|
render json: @poc.to_json
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -18,5 +20,4 @@ class CvemonCvesController < ApplicationController
|
||||||
@cves_for_year = CvemonCve.from_year(params[:year])
|
@cves_for_year = CvemonCve.from_year(params[:year])
|
||||||
render json: @cves_for_year.to_json
|
render json: @cves_for_year.to_json
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class CvesController < ApplicationController
|
class CvesController < ApplicationController
|
||||||
def index
|
def index
|
||||||
@cves = Cve.all
|
@cves = Cve.all
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class GithubAdvisoriesController < ApplicationController
|
class GithubAdvisoriesController < ApplicationController
|
||||||
def index
|
def index
|
||||||
@advisories = GithubAdvisory.all
|
@advisories = GithubAdvisory.all
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class GithubPocsController < ApplicationController
|
class GithubPocsController < ApplicationController
|
||||||
def index
|
def index
|
||||||
@pocs = GithubPoc.all
|
@pocs = GithubPoc.all
|
||||||
|
@ -5,12 +7,12 @@ class GithubPocsController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@poc = GithubPoc.find_by(:id => params[:id])
|
@poc = GithubPoc.find_by(id: params[:id])
|
||||||
render json: @poc.to_json
|
render json: @poc.to_json
|
||||||
end
|
end
|
||||||
|
|
||||||
def show_for_cve
|
def show_for_cve
|
||||||
@poc = GithubPoc.where(:cve_id => params[:cve_id])
|
@poc = GithubPoc.where(cve_id: params[:cve_id])
|
||||||
render json: @poc.to_json
|
render json: @poc.to_json
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -18,5 +20,4 @@ class GithubPocsController < ApplicationController
|
||||||
@cves_for_year = GithubPoc.from_year(params[:year])
|
@cves_for_year = GithubPoc.from_year(params[:year])
|
||||||
render json: @cves_for_year.to_json
|
render json: @cves_for_year.to_json
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class GithubUsersController < ApplicationController
|
class GithubUsersController < ApplicationController
|
||||||
def index
|
def index
|
||||||
@users = GithubUser.all
|
@users = GithubUser.all
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class InthewildCveExploitsController < ApplicationController
|
class InthewildCveExploitsController < ApplicationController
|
||||||
def index
|
def index
|
||||||
@cves = InthewildCveExploit.all
|
@cves = InthewildCveExploit.all
|
||||||
|
@ -5,7 +7,7 @@ class InthewildCveExploitsController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@cve = InthewildCveExploit.find_by(:cve_id => params[:cve_id])
|
@cve = InthewildCveExploit.find_by(cve_id: params[:cve_id])
|
||||||
render json: @cve.to_json
|
render json: @cve.to_json
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class TrickestPocCvesController < ApplicationController
|
class TrickestPocCvesController < ApplicationController
|
||||||
def index
|
def index
|
||||||
@pocs = TrickestPocCve.all
|
@pocs = TrickestPocCve.all
|
||||||
|
@ -5,12 +7,12 @@ class TrickestPocCvesController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@poc = TrickestPocCve.find_by(:id => params[:id])
|
@poc = TrickestPocCve.find_by(id: params[:id])
|
||||||
render json: @poc.to_json
|
render json: @poc.to_json
|
||||||
end
|
end
|
||||||
|
|
||||||
def show_for_cve
|
def show_for_cve
|
||||||
@poc = TrickestPocCve.where(:cve_id => params[:cve_id])
|
@poc = TrickestPocCve.where(cve_id: params[:cve_id])
|
||||||
render json: @poc.to_json
|
render json: @poc.to_json
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -18,5 +20,4 @@ class TrickestPocCvesController < ApplicationController
|
||||||
@cves_for_year = TrickestPocCve.from_year(params[:year])
|
@cves_for_year = TrickestPocCve.from_year(params[:year])
|
||||||
render json: @cves_for_year.to_json
|
render json: @cves_for_year.to_json
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module ApplicationHelper
|
module ApplicationHelper
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module CpesHelper
|
module CpesHelper
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module CvesHelper
|
module CvesHelper
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class ApplicationJob < ActiveJob::Base
|
class ApplicationJob < ActiveJob::Base
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class ApplicationMailer < ActionMailer::Base
|
class ApplicationMailer < ActionMailer::Base
|
||||||
default from: 'from@example.com'
|
default from: 'from@example.com'
|
||||||
layout 'mailer'
|
layout 'mailer'
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class ApplicationRecord < ActiveRecord::Base
|
class ApplicationRecord < ActiveRecord::Base
|
||||||
self.abstract_class = true
|
self.abstract_class = true
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Cna < ActiveRecord::Base
|
class Cna < ActiveRecord::Base
|
||||||
def self.find_by_cna_id(cna_id)
|
def self.find_by_cna_id(cna_id)
|
||||||
find_by(:cna_id => cna_id)
|
find_by(cna_id: cna_id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1 +1,3 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Cpe < ActiveRecord::Base; end
|
class Cpe < ActiveRecord::Base; end
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Cve < ActiveRecord::Base
|
class Cve < ActiveRecord::Base
|
||||||
scope :with_reserved, -> { where("cve_data_meta->>'STATE' = 'RESERVED'") }
|
scope :with_reserved, -> { where("cve_data_meta->>'STATE' = 'RESERVED'") }
|
||||||
scope :without_reserved, -> { where.not("cve_data_meta->>'STATE' = 'RESERVED'") }
|
scope :without_reserved, -> { where.not("cve_data_meta->>'STATE' = 'RESERVED'") }
|
||||||
|
@ -7,10 +9,10 @@ class Cve < ActiveRecord::Base
|
||||||
scope :without_public, -> { where.not("cve_data_meta->>'STATE' = 'PUBLIC'") }
|
scope :without_public, -> { where.not("cve_data_meta->>'STATE' = 'PUBLIC'") }
|
||||||
|
|
||||||
def self.find_by_id(id)
|
def self.find_by_id(id)
|
||||||
find_by(:cve_id => id)
|
find_by(cve_id: id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.from_year(year)
|
def self.from_year(year)
|
||||||
where("cve_id LIKE ?", "CVE-#{year}-%")
|
where('cve_id LIKE ?', "CVE-#{year}-%")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class CvemonCve < ActiveRecord::Base
|
class CvemonCve < ActiveRecord::Base
|
||||||
def self.from_year(year)
|
def self.from_year(year)
|
||||||
where("cve_id LIKE ?", "CVE-#{year}-%")
|
where('cve_id LIKE ?', "CVE-#{year}-%")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
class GithubAdvisory< ActiveRecord::Base
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class GithubAdvisory < ActiveRecord::Base
|
||||||
scope :github_reviewed, -> { where("database_specific->>'github_reviewed' = 'true'") }
|
scope :github_reviewed, -> { where("database_specific->>'github_reviewed' = 'true'") }
|
||||||
scope :unreviewed, -> { where("database_specific->>'github_reviewed' = 'false'") }
|
scope :unreviewed, -> { where("database_specific->>'github_reviewed' = 'false'") }
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class GithubPoc < ActiveRecord::Base
|
class GithubPoc < ActiveRecord::Base
|
||||||
def self.from_year(year)
|
def self.from_year(year)
|
||||||
where("cve_id LIKE ?", "CVE-#{year}-%")
|
where('cve_id LIKE ?', "CVE-#{year}-%")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
class GithubUser< ActiveRecord::Base
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class GithubUser < ActiveRecord::Base
|
||||||
def self.find_by_username(username)
|
def self.find_by_username(username)
|
||||||
find_by(login: username)
|
find_by(login: username)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class InthewildCveExploit < ActiveRecord::Base
|
class InthewildCveExploit < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class TrickestPocCve < ActiveRecord::Base
|
class TrickestPocCve < ActiveRecord::Base
|
||||||
def self.from_year(year)
|
def self.from_year(year)
|
||||||
where("cve_id LIKE ?", "CVE-#{year}-%")
|
where('cve_id LIKE ?', "CVE-#{year}-%")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
require '/data_importer/lib/importers/cna_importer.rb'
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require '/data_importer/lib/importers/cna_importer'
|
||||||
|
|
||||||
class CnaImporterWorker
|
class CnaImporterWorker
|
||||||
include Faktory::Job
|
include Faktory::Job
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
require '/data_importer/lib/importers/cpe_importer.rb'
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require '/data_importer/lib/importers/cpe_importer'
|
||||||
|
|
||||||
class CpeImporterWorker
|
class CpeImporterWorker
|
||||||
include Faktory::Job
|
include Faktory::Job
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
require '/data_importer/lib/importers/cve_list_importer.rb'
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require '/data_importer/lib/importers/cve_list_importer'
|
||||||
|
|
||||||
class CveListImporterWorker
|
class CveListImporterWorker
|
||||||
include Faktory::Job
|
include Faktory::Job
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
require '/data_importer/lib/importers/cvemon_cve_importer.rb'
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require '/data_importer/lib/importers/cvemon_cve_importer'
|
||||||
|
|
||||||
class CvemonCveImporterWorker
|
class CvemonCveImporterWorker
|
||||||
include Faktory::Job
|
include Faktory::Job
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
require '/data_importer/lib/importers/github_advisory_importer.rb'
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require '/data_importer/lib/importers/github_advisory_importer'
|
||||||
|
|
||||||
class GithubAdvisoryImporterWorker
|
class GithubAdvisoryImporterWorker
|
||||||
include Faktory::Job
|
include Faktory::Job
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
require '/data_importer/lib/importers/github_user_importer.rb'
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require '/data_importer/lib/importers/github_user_importer'
|
||||||
|
|
||||||
class GithubUserImporterWorker
|
class GithubUserImporterWorker
|
||||||
include Faktory::Job
|
include Faktory::Job
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
require '/data_importer/lib/importers/inthewild_cve_exploit_importer.rb'
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require '/data_importer/lib/importers/inthewild_cve_exploit_importer'
|
||||||
|
|
||||||
class InthewildCveExploitImporterWorker
|
class InthewildCveExploitImporterWorker
|
||||||
include Faktory::Job
|
include Faktory::Job
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
require '/data_importer/lib/importers/poc_in_github_importer.rb'
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require '/data_importer/lib/importers/poc_in_github_importer'
|
||||||
|
|
||||||
class PocInGithubImporterWorker
|
class PocInGithubImporterWorker
|
||||||
include Faktory::Job
|
include Faktory::Job
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
require '/data_importer/lib/importers/trickest_poc_cve_importer.rb'
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require '/data_importer/lib/importers/trickest_poc_cve_importer'
|
||||||
|
|
||||||
class TrickestPocCveImporterWorker
|
class TrickestPocCveImporterWorker
|
||||||
include Faktory::Job
|
include Faktory::Job
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
|
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
|
||||||
load Gem.bin_path('bundler', 'bundle')
|
load Gem.bin_path('bundler', 'bundle')
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
APP_PATH = File.expand_path('../config/application', __dir__)
|
APP_PATH = File.expand_path('../config/application', __dir__)
|
||||||
require_relative '../config/boot'
|
require_relative '../config/boot'
|
||||||
require 'rails/commands'
|
require 'rails/commands'
|
||||||
|
|
2
bin/rake
2
bin/rake
|
@ -1,4 +1,6 @@
|
||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative '../config/boot'
|
require_relative '../config/boot'
|
||||||
require 'rake'
|
require 'rake'
|
||||||
Rake.application.run
|
Rake.application.run
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'fileutils'
|
require 'fileutils'
|
||||||
include FileUtils
|
include FileUtils
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'fileutils'
|
require 'fileutils'
|
||||||
include FileUtils
|
include FileUtils
|
||||||
|
|
||||||
|
|
12
bin/yarn
12
bin/yarn
|
@ -1,11 +1,11 @@
|
||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
APP_ROOT = File.expand_path('..', __dir__)
|
APP_ROOT = File.expand_path('..', __dir__)
|
||||||
Dir.chdir(APP_ROOT) do
|
Dir.chdir(APP_ROOT) do
|
||||||
begin
|
exec 'yarnpkg', *ARGV
|
||||||
exec "yarnpkg", *ARGV
|
rescue Errno::ENOENT
|
||||||
rescue Errno::ENOENT
|
warn 'Yarn executable was not detected in the system.'
|
||||||
$stderr.puts "Yarn executable was not detected in the system."
|
warn 'Download Yarn at https://yarnpkg.com/en/docs/install'
|
||||||
$stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
|
|
||||||
exit 1
|
exit 1
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
# This file is used by Rack-based servers to start the application.
|
# This file is used by Rack-based servers to start the application.
|
||||||
|
|
||||||
require_relative 'config/environment'
|
require_relative 'config/environment'
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative 'boot'
|
require_relative 'boot'
|
||||||
|
|
||||||
require 'rails/all'
|
require 'rails/all'
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
|
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
|
||||||
|
|
||||||
require 'bundler/setup' # Set up gems listed in the Gemfile.
|
require 'bundler/setup' # Set up gems listed in the Gemfile.
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
# Load the Rails application.
|
# Load the Rails application.
|
||||||
require_relative 'application'
|
require_relative 'application'
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
Rails.application.configure do
|
Rails.application.configure do
|
||||||
# Settings specified here will take precedence over those in config/application.rb.
|
# Settings specified here will take precedence over those in config/application.rb.
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
Rails.application.configure do
|
Rails.application.configure do
|
||||||
# Settings specified here will take precedence over those in config/application.rb.
|
# Settings specified here will take precedence over those in config/application.rb.
|
||||||
|
|
||||||
|
@ -54,7 +56,7 @@ Rails.application.configure do
|
||||||
config.log_level = :debug
|
config.log_level = :debug
|
||||||
|
|
||||||
# Prepend all log lines with the following tags.
|
# Prepend all log lines with the following tags.
|
||||||
config.log_tags = [ :request_id ]
|
config.log_tags = [:request_id]
|
||||||
|
|
||||||
# Use a different cache store in production.
|
# Use a different cache store in production.
|
||||||
# config.cache_store = :mem_cache_store
|
# config.cache_store = :mem_cache_store
|
||||||
|
@ -83,8 +85,8 @@ Rails.application.configure do
|
||||||
# require 'syslog/logger'
|
# require 'syslog/logger'
|
||||||
# config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
|
# config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
|
||||||
|
|
||||||
if ENV["RAILS_LOG_TO_STDOUT"].present?
|
if ENV['RAILS_LOG_TO_STDOUT'].present?
|
||||||
logger = ActiveSupport::Logger.new(STDOUT)
|
logger = ActiveSupport::Logger.new($stdout)
|
||||||
logger.formatter = config.log_formatter
|
logger.formatter = config.log_formatter
|
||||||
config.logger = ActiveSupport::TaggedLogging.new(logger)
|
config.logger = ActiveSupport::TaggedLogging.new(logger)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
Rails.application.configure do
|
Rails.application.configure do
|
||||||
# Settings specified here will take precedence over those in config/application.rb.
|
# Settings specified here will take precedence over those in config/application.rb.
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
# Be sure to restart your server when you modify this file.
|
# Be sure to restart your server when you modify this file.
|
||||||
|
|
||||||
# ActiveSupport::Reloader.to_prepare do
|
# ActiveSupport::Reloader.to_prepare do
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
# Be sure to restart your server when you modify this file.
|
# Be sure to restart your server when you modify this file.
|
||||||
|
|
||||||
# Version of your assets, change this if you want to expire all your assets.
|
# Version of your assets, change this if you want to expire all your assets.
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
# Be sure to restart your server when you modify this file.
|
# Be sure to restart your server when you modify this file.
|
||||||
|
|
||||||
# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
|
# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
# Be sure to restart your server when you modify this file.
|
# Be sure to restart your server when you modify this file.
|
||||||
|
|
||||||
# Define an application-wide content security policy
|
# Define an application-wide content security policy
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
# Be sure to restart your server when you modify this file.
|
# Be sure to restart your server when you modify this file.
|
||||||
|
|
||||||
# Specify a serializer for the signed and encrypted cookie jars.
|
# Specify a serializer for the signed and encrypted cookie jars.
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
# Be sure to restart your server when you modify this file.
|
# Be sure to restart your server when you modify this file.
|
||||||
|
|
||||||
# Configure sensitive parameters which will be filtered from the log file.
|
# Configure sensitive parameters which will be filtered from the log file.
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
# Be sure to restart your server when you modify this file.
|
# Be sure to restart your server when you modify this file.
|
||||||
|
|
||||||
# Add new inflection rules using the following format. Inflections
|
# Add new inflection rules using the following format. Inflections
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
# Be sure to restart your server when you modify this file.
|
# Be sure to restart your server when you modify this file.
|
||||||
|
|
||||||
# Add new mime types for use in respond_to blocks:
|
# Add new mime types for use in respond_to blocks:
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'twitter'
|
require 'twitter'
|
||||||
require 'tweetkit'
|
require 'tweetkit'
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
# Be sure to restart your server when you modify this file.
|
# Be sure to restart your server when you modify this file.
|
||||||
|
|
||||||
# This file contains settings for ActionController::ParamsWrapper which
|
# This file contains settings for ActionController::ParamsWrapper which
|
||||||
|
|
|
@ -1,19 +1,21 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
# Puma can serve each request in a thread from an internal thread pool.
|
# Puma can serve each request in a thread from an internal thread pool.
|
||||||
# The `threads` method setting takes two numbers: a minimum and maximum.
|
# The `threads` method setting takes two numbers: a minimum and maximum.
|
||||||
# Any libraries that use thread pools should be configured to match
|
# Any libraries that use thread pools should be configured to match
|
||||||
# the maximum value specified for Puma. Default is set to 5 threads for minimum
|
# the maximum value specified for Puma. Default is set to 5 threads for minimum
|
||||||
# and maximum; this matches the default thread size of Active Record.
|
# and maximum; this matches the default thread size of Active Record.
|
||||||
#
|
#
|
||||||
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
|
threads_count = ENV.fetch('RAILS_MAX_THREADS', 5)
|
||||||
threads threads_count, threads_count
|
threads threads_count, threads_count
|
||||||
|
|
||||||
# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
|
# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
|
||||||
#
|
#
|
||||||
port ENV.fetch("PORT") { 3000 }
|
port ENV.fetch('PORT', 3000)
|
||||||
|
|
||||||
# Specifies the `environment` that Puma will run in.
|
# Specifies the `environment` that Puma will run in.
|
||||||
#
|
#
|
||||||
environment ENV.fetch("RAILS_ENV") { "development" }
|
environment ENV.fetch('RAILS_ENV', 'development')
|
||||||
|
|
||||||
# Specifies the number of `workers` to boot in clustered mode.
|
# Specifies the number of `workers` to boot in clustered mode.
|
||||||
# Workers are forked webserver processes. If using threads and workers together
|
# Workers are forked webserver processes. If using threads and workers together
|
||||||
|
|
|
@ -1,39 +1,40 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
Rails.application.routes.draw do
|
Rails.application.routes.draw do
|
||||||
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
|
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
|
||||||
get "/cves", to: "cves#index"
|
get '/cves', to: 'cves#index'
|
||||||
get "/cves/:cve_id", to: "cves#show"
|
get '/cves/:cve_id', to: 'cves#show'
|
||||||
get "/cves/years/:year", to: "cves#show_year"
|
get '/cves/years/:year', to: 'cves#show_year'
|
||||||
|
|
||||||
get "/cpes", to: "cpes#index"
|
get '/cpes', to: 'cpes#index'
|
||||||
get "/cpes/:id", to: "cpes#show"
|
get '/cpes/:id', to: 'cpes#show'
|
||||||
|
|
||||||
get "/github_pocs", to: "github_pocs#index"
|
get '/github_pocs', to: 'github_pocs#index'
|
||||||
get "/github_pocs/:id", to: "github_pocs#show"
|
get '/github_pocs/:id', to: 'github_pocs#show'
|
||||||
get "/github_pocs/cve/:cve_id", to: "github_pocs#show_for_cve"
|
get '/github_pocs/cve/:cve_id', to: 'github_pocs#show_for_cve'
|
||||||
get "/github_pocs/years/:year", to: "github_pocs#show_year"
|
get '/github_pocs/years/:year', to: 'github_pocs#show_year'
|
||||||
|
|
||||||
get "/inthewild_cve_exploits", to: "inthewild_cve_exploits#index"
|
get '/inthewild_cve_exploits', to: 'inthewild_cve_exploits#index'
|
||||||
get "/inthewild_cve_exploits/:cve_id", to: "inthewild_cve_exploits#show"
|
get '/inthewild_cve_exploits/:cve_id', to: 'inthewild_cve_exploits#show'
|
||||||
|
|
||||||
get "/trickest_poc_cves", to: "trickest_poc_cves#index"
|
get '/trickest_poc_cves', to: 'trickest_poc_cves#index'
|
||||||
get "/trickest_poc_cves/:id", to: "trickest_poc_cves#show"
|
get '/trickest_poc_cves/:id', to: 'trickest_poc_cves#show'
|
||||||
get "/trickest_poc_cves/cve/:cve_id", to: "trickest_poc_cves#show_for_cve"
|
get '/trickest_poc_cves/cve/:cve_id', to: 'trickest_poc_cves#show_for_cve'
|
||||||
get "/trickest_poc_cves/years/:year", to: "trickest_poc_cves#show_year"
|
get '/trickest_poc_cves/years/:year', to: 'trickest_poc_cves#show_year'
|
||||||
|
|
||||||
get "/cvemon_cves", to: "cvemon_cves#index"
|
get '/cvemon_cves', to: 'cvemon_cves#index'
|
||||||
get "/cvemon_cves/:id", to: "cvemon_cves#show"
|
get '/cvemon_cves/:id', to: 'cvemon_cves#show'
|
||||||
get "/cvemon_cves/cve/:cve_id", to: "cvemon_cves#show_for_cve"
|
get '/cvemon_cves/cve/:cve_id', to: 'cvemon_cves#show_for_cve'
|
||||||
get "/cvemon_cves/years/:year", to: "cvemon_cves#show_year"
|
get '/cvemon_cves/years/:year', to: 'cvemon_cves#show_year'
|
||||||
|
|
||||||
get "/cnas", to: "cnas#index"
|
get '/cnas', to: 'cnas#index'
|
||||||
get "/cnas/:id", to: "cnas#show"
|
get '/cnas/:id', to: 'cnas#show'
|
||||||
get "/cnas/cna/:cna_id", to: "cnas#show_for_cna"
|
get '/cnas/cna/:cna_id', to: 'cnas#show_for_cna'
|
||||||
get "/cnas/organization_name/:organization_name", to: "cnas#show_for_orgname"
|
get '/cnas/organization_name/:organization_name', to: 'cnas#show_for_orgname'
|
||||||
|
|
||||||
get "/github_advisories", to: "github_advisories#index"
|
get '/github_advisories', to: 'github_advisories#index'
|
||||||
get "/github_advisories/:ghsa_id", to: "github_advisories#show"
|
get '/github_advisories/:ghsa_id', to: 'github_advisories#show'
|
||||||
|
|
||||||
get "/github_users", to: "github_users#index"
|
|
||||||
get "/github_users/:username", to: "github_users#show"
|
|
||||||
|
|
||||||
|
get '/github_users', to: 'github_users#index'
|
||||||
|
get '/github_users/:username', to: 'github_users#show'
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
%w[
|
%w[
|
||||||
.ruby-version
|
.ruby-version
|
||||||
.rbenv-vars
|
.rbenv-vars
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class CreateCves < ActiveRecord::Migration[5.2]
|
class CreateCves < ActiveRecord::Migration[5.2]
|
||||||
def change
|
def change
|
||||||
create_table :cves do |t|
|
create_table :cves do |t|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class CreateCpes < ActiveRecord::Migration[7.0]
|
class CreateCpes < ActiveRecord::Migration[7.0]
|
||||||
def change
|
def change
|
||||||
create_table :cpes do |t|
|
create_table :cpes do |t|
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class GithubPocs < ActiveRecord::Migration[7.0]
|
class GithubPocs < ActiveRecord::Migration[7.0]
|
||||||
def change
|
def change
|
||||||
create_table :github_pocs do |t|
|
create_table :github_pocs do |t|
|
||||||
t.integer :github_poc_id
|
t.integer :github_poc_id
|
||||||
t.index :github_poc_id, unique: true
|
t.index :github_poc_id, unique: true
|
||||||
t.string :cve_id, default: "None"
|
t.string :cve_id, default: 'None'
|
||||||
t.string :name
|
t.string :name
|
||||||
t.string :full_name
|
t.string :full_name
|
||||||
t.jsonb :owner
|
t.jsonb :owner
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class InthewildCveExploits < ActiveRecord::Migration[7.0]
|
class InthewildCveExploits < ActiveRecord::Migration[7.0]
|
||||||
def change
|
def change
|
||||||
create_table :inthewild_cve_exploits do |t|
|
create_table :inthewild_cve_exploits do |t|
|
||||||
t.string :cve_id
|
t.string :cve_id
|
||||||
# i think maybe making a string is better for now for the earliestReport data
|
# i think maybe making a string is better for now for the earliestReport data
|
||||||
t.string :earliest_report
|
t.string :earliest_report
|
||||||
#t.date :earliest_report
|
# t.date :earliest_report
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class CreateTrickestPocCves < ActiveRecord::Migration[7.0]
|
class CreateTrickestPocCves < ActiveRecord::Migration[7.0]
|
||||||
def change
|
def change
|
||||||
create_table :trickest_poc_cves do |t|
|
create_table :trickest_poc_cves do |t|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class CreateCvemonCves < ActiveRecord::Migration[7.0]
|
class CreateCvemonCves < ActiveRecord::Migration[7.0]
|
||||||
def change
|
def change
|
||||||
create_table :cvemon_cves do |t|
|
create_table :cvemon_cves do |t|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class CreateCnas < ActiveRecord::Migration[7.0]
|
class CreateCnas < ActiveRecord::Migration[7.0]
|
||||||
def change
|
def change
|
||||||
create_table :cnas do |t|
|
create_table :cnas do |t|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class CreateGithubUsers < ActiveRecord::Migration[7.0]
|
class CreateGithubUsers < ActiveRecord::Migration[7.0]
|
||||||
def change
|
def change
|
||||||
create_table :github_users do |t|
|
create_table :github_users do |t|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class CreateGithubAdvisories < ActiveRecord::Migration[7.0]
|
class CreateGithubAdvisories < ActiveRecord::Migration[7.0]
|
||||||
def change
|
def change
|
||||||
create_table :github_advisories do |t|
|
create_table :github_advisories do |t|
|
||||||
|
|
199
db/schema.rb
199
db/schema.rb
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
# This file is auto-generated from the current state of the database. Instead
|
# This file is auto-generated from the current state of the database. Instead
|
||||||
# of editing this file, please use the migrations feature of Active Record to
|
# of editing this file, please use the migrations feature of Active Record to
|
||||||
# incrementally modify your database, and then regenerate this schema definition.
|
# incrementally modify your database, and then regenerate this schema definition.
|
||||||
|
@ -10,122 +12,121 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema[7.0].define(version: 2022_04_11_181501) do
|
ActiveRecord::Schema[7.0].define(version: 20_220_411_181_501) do
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension 'plpgsql'
|
||||||
|
|
||||||
create_table "cnas", force: :cascade do |t|
|
create_table 'cnas', force: :cascade do |t|
|
||||||
t.string "short_name"
|
t.string 'short_name'
|
||||||
t.string "cna_id"
|
t.string 'cna_id'
|
||||||
t.string "organization_name"
|
t.string 'organization_name'
|
||||||
t.string "scope"
|
t.string 'scope'
|
||||||
t.jsonb "contact"
|
t.jsonb 'contact'
|
||||||
t.jsonb "disclosure_policy"
|
t.jsonb 'disclosure_policy'
|
||||||
t.jsonb "security_advisories"
|
t.jsonb 'security_advisories'
|
||||||
t.string "resources", array: true
|
t.string 'resources', array: true
|
||||||
t.jsonb "cna"
|
t.jsonb 'cna'
|
||||||
t.string "country"
|
t.string 'country'
|
||||||
t.datetime "created_at", null: false
|
t.datetime 'created_at', null: false
|
||||||
t.datetime "updated_at", null: false
|
t.datetime 'updated_at', null: false
|
||||||
t.index ["cna_id"], name: "index_cnas_on_cna_id", unique: true
|
t.index ['cna_id'], name: 'index_cnas_on_cna_id', unique: true
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "cpes", force: :cascade do |t|
|
create_table 'cpes', force: :cascade do |t|
|
||||||
t.string "status"
|
t.string 'status'
|
||||||
t.date "modification_date"
|
t.date 'modification_date'
|
||||||
t.integer "nvd_id"
|
t.integer 'nvd_id'
|
||||||
t.jsonb "references"
|
t.jsonb 'references'
|
||||||
t.string "title"
|
t.string 'title'
|
||||||
t.string "name"
|
t.string 'name'
|
||||||
t.index ["nvd_id"], name: "index_cpes_on_nvd_id", unique: true
|
t.index ['nvd_id'], name: 'index_cpes_on_nvd_id', unique: true
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "cvemon_cves", force: :cascade do |t|
|
create_table 'cvemon_cves', force: :cascade do |t|
|
||||||
t.string "cve_id"
|
t.string 'cve_id'
|
||||||
t.string "urls", array: true
|
t.string 'urls', array: true
|
||||||
t.datetime "created_at", null: false
|
t.datetime 'created_at', null: false
|
||||||
t.datetime "updated_at", null: false
|
t.datetime 'updated_at', null: false
|
||||||
t.index ["cve_id"], name: "index_cvemon_cves_on_cve_id", unique: true
|
t.index ['cve_id'], name: 'index_cvemon_cves_on_cve_id', unique: true
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "cves", force: :cascade do |t|
|
create_table 'cves', force: :cascade do |t|
|
||||||
t.jsonb "cve_data_meta"
|
t.jsonb 'cve_data_meta'
|
||||||
t.string "cve_id"
|
t.string 'cve_id'
|
||||||
t.jsonb "affects"
|
t.jsonb 'affects'
|
||||||
t.string "data_format"
|
t.string 'data_format'
|
||||||
t.string "data_type"
|
t.string 'data_type'
|
||||||
t.string "data_version"
|
t.string 'data_version'
|
||||||
t.jsonb "description"
|
t.jsonb 'description'
|
||||||
t.jsonb "impact"
|
t.jsonb 'impact'
|
||||||
t.jsonb "problemtype"
|
t.jsonb 'problemtype'
|
||||||
t.jsonb "references"
|
t.jsonb 'references'
|
||||||
t.jsonb "source"
|
t.jsonb 'source'
|
||||||
t.datetime "created_at", precision: nil, null: false
|
t.datetime 'created_at', precision: nil, null: false
|
||||||
t.datetime "updated_at", precision: nil, null: false
|
t.datetime 'updated_at', precision: nil, null: false
|
||||||
t.index ["cve_id"], name: "index_cves_on_cve_id", unique: true
|
t.index ['cve_id'], name: 'index_cves_on_cve_id', unique: true
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "github_advisories", force: :cascade do |t|
|
create_table 'github_advisories', force: :cascade do |t|
|
||||||
t.string "schema_version"
|
t.string 'schema_version'
|
||||||
t.string "ghsa_id"
|
t.string 'ghsa_id'
|
||||||
t.date "modified"
|
t.date 'modified'
|
||||||
t.date "published"
|
t.date 'published'
|
||||||
t.string "aliases", array: true
|
t.string 'aliases', array: true
|
||||||
t.string "summary"
|
t.string 'summary'
|
||||||
t.string "details"
|
t.string 'details'
|
||||||
t.jsonb "severity"
|
t.jsonb 'severity'
|
||||||
t.jsonb "affected"
|
t.jsonb 'affected'
|
||||||
t.jsonb "references"
|
t.jsonb 'references'
|
||||||
t.jsonb "database_specific"
|
t.jsonb 'database_specific'
|
||||||
t.index ["ghsa_id"], name: "index_github_advisories_on_ghsa_id", unique: true
|
t.index ['ghsa_id'], name: 'index_github_advisories_on_ghsa_id', unique: true
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "github_pocs", force: :cascade do |t|
|
create_table 'github_pocs', force: :cascade do |t|
|
||||||
t.integer "github_poc_id"
|
t.integer 'github_poc_id'
|
||||||
t.string "cve_id", default: "None"
|
t.string 'cve_id', default: 'None'
|
||||||
t.string "name"
|
t.string 'name'
|
||||||
t.string "full_name"
|
t.string 'full_name'
|
||||||
t.jsonb "owner"
|
t.jsonb 'owner'
|
||||||
t.string "html_url"
|
t.string 'html_url'
|
||||||
t.string "description"
|
t.string 'description'
|
||||||
t.boolean "fork"
|
t.boolean 'fork'
|
||||||
t.date "created_at"
|
t.date 'created_at'
|
||||||
t.date "updated_at"
|
t.date 'updated_at'
|
||||||
t.date "pushed_at"
|
t.date 'pushed_at'
|
||||||
t.integer "stargazers_count"
|
t.integer 'stargazers_count'
|
||||||
t.integer "watchers_count"
|
t.integer 'watchers_count'
|
||||||
t.integer "forks_count"
|
t.integer 'forks_count'
|
||||||
t.boolean "allow_forking"
|
t.boolean 'allow_forking'
|
||||||
t.boolean "is_template"
|
t.boolean 'is_template'
|
||||||
t.string "topics", array: true
|
t.string 'topics', array: true
|
||||||
t.string "visibility"
|
t.string 'visibility'
|
||||||
t.integer "forks"
|
t.integer 'forks'
|
||||||
t.integer "watchers"
|
t.integer 'watchers'
|
||||||
t.integer "score"
|
t.integer 'score'
|
||||||
t.index ["github_poc_id"], name: "index_github_pocs_on_github_poc_id", unique: true
|
t.index ['github_poc_id'], name: 'index_github_pocs_on_github_poc_id', unique: true
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "github_users", force: :cascade do |t|
|
create_table 'github_users', force: :cascade do |t|
|
||||||
t.string "github_id"
|
t.string 'github_id'
|
||||||
t.string "login"
|
t.string 'login'
|
||||||
t.string "name"
|
t.string 'name'
|
||||||
t.string "avatar_url"
|
t.string 'avatar_url'
|
||||||
t.string "bio"
|
t.string 'bio'
|
||||||
t.text "bio_html"
|
t.text 'bio_html'
|
||||||
t.string "location"
|
t.string 'location'
|
||||||
t.jsonb "repositories"
|
t.jsonb 'repositories'
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "inthewild_cve_exploits", force: :cascade do |t|
|
create_table 'inthewild_cve_exploits', force: :cascade do |t|
|
||||||
t.string "cve_id"
|
t.string 'cve_id'
|
||||||
t.string "earliest_report"
|
t.string 'earliest_report'
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "trickest_poc_cves", force: :cascade do |t|
|
create_table 'trickest_poc_cves', force: :cascade do |t|
|
||||||
t.string "cve_id"
|
t.string 'cve_id'
|
||||||
t.string "cve_url"
|
t.string 'cve_url'
|
||||||
t.string "description"
|
t.string 'description'
|
||||||
t.string "poc_links", array: true
|
t.string 'poc_links', array: true
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
20
db/seeds.rb
20
db/seeds.rb
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
# This file should contain all the record creation needed to seed the database with its default values.
|
# This file should contain all the record creation needed to seed the database with its default values.
|
||||||
# The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup).
|
# The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup).
|
||||||
#
|
#
|
||||||
|
@ -6,15 +8,15 @@
|
||||||
# movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }])
|
# movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }])
|
||||||
# Character.create(name: 'Luke', movie: movies.first)
|
# Character.create(name: 'Luke', movie: movies.first)
|
||||||
|
|
||||||
require '/data_importer/lib/importers/cpe_importer.rb'
|
require '/data_importer/lib/importers/cpe_importer'
|
||||||
require '/data_importer/lib/importers/cve_list_importer.rb'
|
require '/data_importer/lib/importers/cve_list_importer'
|
||||||
require '/data_importer/lib/importers/poc_in_github_importer.rb'
|
require '/data_importer/lib/importers/poc_in_github_importer'
|
||||||
require '/data_importer/lib/importers/inthewild_cve_exploit_importer.rb'
|
require '/data_importer/lib/importers/inthewild_cve_exploit_importer'
|
||||||
require '/data_importer/lib/importers/trickest_poc_cve_importer.rb'
|
require '/data_importer/lib/importers/trickest_poc_cve_importer'
|
||||||
require '/data_importer/lib/importers/cvemon_cve_importer.rb'
|
require '/data_importer/lib/importers/cvemon_cve_importer'
|
||||||
require '/data_importer/lib/importers/cna_importer.rb'
|
require '/data_importer/lib/importers/cna_importer'
|
||||||
require '/data_importer/lib/importers/github_advisory_importer.rb'
|
require '/data_importer/lib/importers/github_advisory_importer'
|
||||||
require '/data_importer/lib/importers/github_user_importer.rb'
|
require '/data_importer/lib/importers/github_user_importer'
|
||||||
|
|
||||||
def line_sep
|
def line_sep
|
||||||
puts '----------' * 12
|
puts '----------' * 12
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
require "graphql/client"
|
# frozen_string_literal: true
|
||||||
require "graphql/client/http"
|
|
||||||
|
require 'graphql/client'
|
||||||
|
require 'graphql/client/http'
|
||||||
|
|
||||||
module GithubApi
|
module GithubApi
|
||||||
GITHUB_ACCESS_TOKEN = ENV['github_api_token']
|
GITHUB_ACCESS_TOKEN = ENV['github_api_token']
|
||||||
URL = 'https://api.github.com/graphql'
|
URL = 'https://api.github.com/graphql'
|
||||||
|
|
||||||
HttpAdapter = GraphQL::Client::HTTP.new(URL) do
|
HttpAdapter = GraphQL::Client::HTTP.new(URL) do
|
||||||
def headers(context)
|
def headers(_context)
|
||||||
{
|
{
|
||||||
"Authorization" => "Bearer #{GITHUB_ACCESS_TOKEN}",
|
'Authorization' => "Bearer #{GITHUB_ACCESS_TOKEN}",
|
||||||
"User-Agent" => 'Ruby'
|
'User-Agent' => 'Ruby'
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
require '/data_importer/lib/github_api/github_api.rb'
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require '/data_importer/lib/github_api/github_api'
|
||||||
|
|
||||||
module GithubApi
|
module GithubApi
|
||||||
class OwnerRepos
|
class OwnerRepos
|
||||||
|
@ -56,14 +58,14 @@ module GithubApi
|
||||||
GRAPHQL
|
GRAPHQL
|
||||||
|
|
||||||
def self.find(username)
|
def self.find(username)
|
||||||
#Retryable.retryable(tries: 3, on: QueryExecutionError, sleep: lambda { |n| 4**n } ) do
|
# Retryable.retryable(tries: 3, on: QueryExecutionError, sleep: lambda { |n| 4**n } ) do
|
||||||
response = GithubApi::Client.query(OwnerReposQuery, variables: { owner: username })
|
response = GithubApi::Client.query(OwnerReposQuery, variables: { owner: username })
|
||||||
if response.errors.any?
|
if response.errors.any?
|
||||||
raise QueryExecutionError.new(response.errors[:data].join(", "))
|
raise QueryExecutionError, response.errors[:data].join(', ')
|
||||||
else
|
else
|
||||||
response.data.repository_owner.repositories.nodes.map(&:to_h)
|
response.data.repository_owner.repositories.nodes.map(&:to_h)
|
||||||
end
|
end
|
||||||
#end
|
# end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
require '/data_importer/lib/github_api/github_api.rb'
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require '/data_importer/lib/github_api/github_api'
|
||||||
module GithubApi
|
module GithubApi
|
||||||
class SecurityAdvisory
|
class SecurityAdvisory
|
||||||
SecurityAdvisoryQuery = GithubApi::Client.parse <<-'GRAPHQL'
|
SecurityAdvisoryQuery = GithubApi::Client.parse <<-'GRAPHQL'
|
||||||
query($ghsa_id: String!) {
|
query($ghsa_id: String!) {
|
||||||
securityAdvisory(ghsaId: $ghsa_id) {
|
securityAdvisory(ghsaId: $ghsa_id) {
|
||||||
|
@ -45,16 +47,16 @@ class SecurityAdvisory
|
||||||
GRAPHQL
|
GRAPHQL
|
||||||
|
|
||||||
def self.find(ghsa_id)
|
def self.find(ghsa_id)
|
||||||
#Retryable.retryable(tries: 3, on: QueryExecutionError, sleep: lambda { |n| 4**n } ) do
|
# Retryable.retryable(tries: 3, on: QueryExecutionError, sleep: lambda { |n| 4**n } ) do
|
||||||
response = GithubApi::Client.query(SecurityAdvisoryQuery, variables: { ghsa_id: ghsa_id })
|
response = GithubApi::Client.query(SecurityAdvisoryQuery, variables: { ghsa_id: ghsa_id })
|
||||||
if response.errors.any?
|
if response.errors.any?
|
||||||
raise QueryExecutionError.new(response.errors[:data].join(", "))
|
raise QueryExecutionError, response.errors[:data].join(', ')
|
||||||
else
|
else
|
||||||
response.data.security_advisory
|
response.data.security_advisory
|
||||||
end
|
end
|
||||||
#end
|
# end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
class QueryExecutionError < StandardError; end
|
class QueryExecutionError < StandardError; end
|
|
@ -1,6 +1,8 @@
|
||||||
require '/data_importer/lib/github_api/github_api.rb'
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require '/data_importer/lib/github_api/github_api'
|
||||||
module GithubApi
|
module GithubApi
|
||||||
class User
|
class User
|
||||||
UserProfileQuery = GithubApi::Client.parse <<-'GRAPHQL'
|
UserProfileQuery = GithubApi::Client.parse <<-'GRAPHQL'
|
||||||
query($username: String!) {
|
query($username: String!) {
|
||||||
user(login: $username) {
|
user(login: $username) {
|
||||||
|
@ -16,16 +18,16 @@ class User
|
||||||
GRAPHQL
|
GRAPHQL
|
||||||
|
|
||||||
def self.find(username)
|
def self.find(username)
|
||||||
#Retryable.retryable(tries: 3, on: QueryExecutionError, sleep: lambda { |n| 4**n } ) do
|
# Retryable.retryable(tries: 3, on: QueryExecutionError, sleep: lambda { |n| 4**n } ) do
|
||||||
response = GithubApi::Client.query(UserProfileQuery, variables: { username: username })
|
response = GithubApi::Client.query(UserProfileQuery, variables: { username: username })
|
||||||
if response.errors.any?
|
if response.errors.any?
|
||||||
raise QueryExecutionError.new(response.errors[:data].join(", "))
|
raise QueryExecutionError, response.errors[:data].join(', ')
|
||||||
else
|
else
|
||||||
response.data.user
|
response.data.user
|
||||||
end
|
end
|
||||||
#end
|
# end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
class QueryExecutionError < StandardError; end
|
class QueryExecutionError < StandardError; end
|
|
@ -1,25 +1,27 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'json'
|
require 'json'
|
||||||
require '/data_importer/lib/json_helper.rb'
|
require '/data_importer/lib/json_helper'
|
||||||
require 'rest-client'
|
require 'rest-client'
|
||||||
|
|
||||||
class CnaImporter
|
class CnaImporter
|
||||||
|
EXPECTED_KEYS = %i[
|
||||||
EXPECTED_KEYS = [
|
short_name
|
||||||
:short_name,
|
cna_id
|
||||||
:cna_id,
|
organization_name
|
||||||
:organization_name,
|
scope
|
||||||
:scope,
|
contact
|
||||||
:contact,
|
disclosure_policy
|
||||||
:disclosure_policy,
|
security_advisories
|
||||||
:security_advisories,
|
resources
|
||||||
:resources,
|
cna
|
||||||
:cna,
|
country
|
||||||
:country
|
|
||||||
].freeze
|
].freeze
|
||||||
|
|
||||||
EMPTY_HASH = EXPECTED_KEYS.map {|k| [k, nil] }.to_h.freeze
|
EMPTY_HASH = EXPECTED_KEYS.map { |k| [k, nil] }.to_h.freeze
|
||||||
|
|
||||||
attr_accessor :url
|
attr_accessor :url
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
@url = 'https://raw.githubusercontent.com/CVEProject/cve-website/dev/src/assets/data/CNAsList.json'
|
@url = 'https://raw.githubusercontent.com/CVEProject/cve-website/dev/src/assets/data/CNAsList.json'
|
||||||
end
|
end
|
||||||
|
@ -47,9 +49,8 @@ class CnaImporter
|
||||||
|
|
||||||
def import
|
def import
|
||||||
jsons = get_json
|
jsons = get_json
|
||||||
merged_hashes = jsons.map {|h| h.slice(*EXPECTED_KEYS).reverse_merge(EMPTY_HASH) }
|
merged_hashes = jsons.map { |h| h.slice(*EXPECTED_KEYS).reverse_merge(EMPTY_HASH) }
|
||||||
puts "Now importing CNAs."
|
puts 'Now importing CNAs.'
|
||||||
Cna.upsert_all(merged_hashes, unique_by: :cna_id)
|
Cna.upsert_all(merged_hashes, unique_by: :cna_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -48,8 +48,8 @@ class CpeImporter
|
||||||
node.name == 'cpe-item' && node.node_type == Nokogiri::XML::Reader::TYPE_ELEMENT
|
node.name == 'cpe-item' && node.node_type == Nokogiri::XML::Reader::TYPE_ELEMENT
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.import(bulk_count = 20000, filepath = '/data_importer/data/official-cpe-dictionary_v2.2.xml.gz')
|
def self.import(bulk_count = 20_000, filepath = '/data_importer/data/official-cpe-dictionary_v2.2.xml.gz')
|
||||||
puts "Now importing Cpes."
|
puts 'Now importing Cpes.'
|
||||||
Zlib::GzipReader.open(filepath) do |file|
|
Zlib::GzipReader.open(filepath) do |file|
|
||||||
items = []
|
items = []
|
||||||
Nokogiri::XML::Reader.from_io(file).each do |node|
|
Nokogiri::XML::Reader.from_io(file).each do |node|
|
||||||
|
|
|
@ -1,29 +1,31 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'git'
|
require 'git'
|
||||||
require 'json'
|
require 'json'
|
||||||
require 'date'
|
require 'date'
|
||||||
require '/data_importer/lib/importers/github_repo.rb'
|
require '/data_importer/lib/importers/github_repo'
|
||||||
require '/data_importer/lib/json_helper.rb'
|
require '/data_importer/lib/json_helper'
|
||||||
|
|
||||||
# This class can be used to import cvelist json data from mitre from their github repo
|
# This class can be used to import cvelist json data from mitre from their github repo
|
||||||
class CveListImporter < GithubRepo
|
class CveListImporter < GithubRepo
|
||||||
EXPECTED_KEYS = [
|
EXPECTED_KEYS = %i[
|
||||||
:cve_data_meta,
|
cve_data_meta
|
||||||
:cve_id,
|
cve_id
|
||||||
:affects,
|
affects
|
||||||
:data_format,
|
data_format
|
||||||
:data_type,
|
data_type
|
||||||
:data_version,
|
data_version
|
||||||
:description,
|
description
|
||||||
:impact,
|
impact
|
||||||
:problemtype,
|
problemtype
|
||||||
:references,
|
references
|
||||||
:source
|
source
|
||||||
].freeze
|
].freeze
|
||||||
|
|
||||||
EMPTY_HASH = EXPECTED_KEYS.map {|k| [k, nil] }.to_h.freeze
|
EMPTY_HASH = EXPECTED_KEYS.map { |k| [k, nil] }.to_h.freeze
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
super(repo_url='https://github.com/CVEProject/cvelist.git', repo_path='/data_importer/data/cve_list')
|
super(repo_url = 'https://github.com/CVEProject/cvelist.git', repo_path = '/data_importer/data/cve_list')
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_jsons_for_year(year)
|
def list_jsons_for_year(year)
|
||||||
|
@ -38,7 +40,7 @@ EMPTY_HASH = EXPECTED_KEYS.map {|k| [k, nil] }.to_h.freeze
|
||||||
json_transformed = JsonHelper.deep_transform_keys(json)
|
json_transformed = JsonHelper.deep_transform_keys(json)
|
||||||
add_cve_id_to_json_key(json_transformed)
|
add_cve_id_to_json_key(json_transformed)
|
||||||
end
|
end
|
||||||
hashes.map {|h| h.slice(*EXPECTED_KEYS).reverse_merge(EMPTY_HASH) }
|
hashes.map { |h| h.slice(*EXPECTED_KEYS).reverse_merge(EMPTY_HASH) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_cve_id_to_json_key(json)
|
def add_cve_id_to_json_key(json)
|
||||||
|
@ -53,11 +55,11 @@ EMPTY_HASH = EXPECTED_KEYS.map {|k| [k, nil] }.to_h.freeze
|
||||||
(1999..Date.today.year).map do |year|
|
(1999..Date.today.year).map do |year|
|
||||||
cves = read_jsons_for_year(year)
|
cves = read_jsons_for_year(year)
|
||||||
|
|
||||||
#ids = cves.map { |cve| cve[:cve_id] }
|
# ids = cves.map { |cve| cve[:cve_id] }
|
||||||
#cve_ids_in_db = Cve.where(:cve_id => ids).pluck(:cve_id)
|
# cve_ids_in_db = Cve.where(:cve_id => ids).pluck(:cve_id)
|
||||||
|
|
||||||
#new_cve_ids = ids - cve_ids_in_db
|
# new_cve_ids = ids - cve_ids_in_db
|
||||||
#new_cves = cves.select { |cve| cve if new_cve_ids.include?(cve[:cve_id]) }
|
# new_cves = cves.select { |cve| cve if new_cve_ids.include?(cve[:cve_id]) }
|
||||||
puts "Importing any new CVEs from #{year}"
|
puts "Importing any new CVEs from #{year}"
|
||||||
|
|
||||||
Cve.upsert_all(cves, unique_by: :cve_id)
|
Cve.upsert_all(cves, unique_by: :cve_id)
|
||||||
|
|
|
@ -1,17 +1,20 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rest-client'
|
require 'rest-client'
|
||||||
require 'json'
|
require 'json'
|
||||||
|
|
||||||
class CvemonCveImporter
|
class CvemonCveImporter
|
||||||
attr_accessor :url
|
attr_accessor :url
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
@url = 'https://raw.githubusercontent.com/ARPSyndicate/cvemon/main/data.json'
|
@url = 'https://raw.githubusercontent.com/ARPSyndicate/cvemon/main/data.json'
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_cve_data
|
def get_cve_data
|
||||||
r = RestClient::Request.execute(
|
r = RestClient::Request.execute(
|
||||||
:method => :get,
|
method: :get,
|
||||||
:url => url,
|
url: url,
|
||||||
:headers => {"Content-type": "application/json"}
|
headers: { "Content-type": 'application/json' }
|
||||||
)
|
)
|
||||||
if r.code == 200
|
if r.code == 200
|
||||||
JSON.parse(r.body)
|
JSON.parse(r.body)
|
||||||
|
@ -23,9 +26,9 @@ class CvemonCveImporter
|
||||||
def import
|
def import
|
||||||
feed = get_cve_data
|
feed = get_cve_data
|
||||||
cve_ids = feed.keys
|
cve_ids = feed.keys
|
||||||
puts "Now importing CvemonCves."
|
puts 'Now importing CvemonCves.'
|
||||||
cves = cve_ids.map do |cve_id|
|
cves = cve_ids.map do |cve_id|
|
||||||
{ :cve_id => cve_id, :urls => feed[cve_id] }
|
{ cve_id: cve_id, urls: feed[cve_id] }
|
||||||
end
|
end
|
||||||
CvemonCve.upsert_all(cves, unique_by: :cve_id)
|
CvemonCve.upsert_all(cves, unique_by: :cve_id)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,24 +1,26 @@
|
||||||
require '/data_importer/lib/importers/github_repo.rb'
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require '/data_importer/lib/importers/github_repo'
|
||||||
|
|
||||||
class GithubAdvisoryImporter < GithubRepo
|
class GithubAdvisoryImporter < GithubRepo
|
||||||
# repo has years that begin with 2017 as first GHSA
|
# repo has years that begin with 2017 as first GHSA
|
||||||
YEAR_RANGE = (2017..Date.today.year)
|
YEAR_RANGE = (2017..Date.today.year).freeze
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
super(repo_url='https://github.com/github/advisory-database.git', repo_path='/data_importer/data/github_advisories')
|
super(repo_url = 'https://github.com/github/advisory-database.git', repo_path = '/data_importer/data/github_advisories')
|
||||||
end
|
end
|
||||||
|
|
||||||
def advisory_paths
|
def advisory_paths
|
||||||
advisory_path = "#{repo_path}/advisories"
|
advisory_path = "#{repo_path}/advisories"
|
||||||
{
|
{
|
||||||
:base_path => advisory_path,
|
base_path: advisory_path,
|
||||||
:github_reviewed_path => "#{advisory_path}/github-reviewed",
|
github_reviewed_path: "#{advisory_path}/github-reviewed",
|
||||||
:unreviewed_path => "#{advisory_path}/unreviewed"
|
unreviewed_path: "#{advisory_path}/unreviewed"
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_jsons_for_year(year)
|
def list_jsons_for_year(year)
|
||||||
json_wildcard = "*.json"
|
json_wildcard = '*.json'
|
||||||
github_reviewed_year_fp = "#{advisory_paths[:github_reviewed_path]}/#{year}/*/*"
|
github_reviewed_year_fp = "#{advisory_paths[:github_reviewed_path]}/#{year}/*/*"
|
||||||
unreviewed_year_fp = "#{advisory_paths[:unreviewed_path]}/#{year}/*/*"
|
unreviewed_year_fp = "#{advisory_paths[:unreviewed_path]}/#{year}/*/*"
|
||||||
|
|
||||||
|
@ -26,8 +28,8 @@ class GithubAdvisoryImporter < GithubRepo
|
||||||
unreviewed_jsons_fp = Dir["#{unreviewed_year_fp}/#{json_wildcard}"]
|
unreviewed_jsons_fp = Dir["#{unreviewed_year_fp}/#{json_wildcard}"]
|
||||||
|
|
||||||
{
|
{
|
||||||
:github_reviewed_jsons => github_reviewed_jsons_fp,
|
github_reviewed_jsons: github_reviewed_jsons_fp,
|
||||||
:unreviewed_jsons => unreviewed_jsons_fp
|
unreviewed_jsons: unreviewed_jsons_fp
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -67,7 +69,7 @@ class GithubAdvisoryImporter < GithubRepo
|
||||||
|
|
||||||
def import
|
def import
|
||||||
pull_or_clone
|
pull_or_clone
|
||||||
puts "Now importing GithubAdvisories."
|
puts 'Now importing GithubAdvisories.'
|
||||||
YEAR_RANGE.each do |year|
|
YEAR_RANGE.each do |year|
|
||||||
puts "Importing advisory data from #{year}"
|
puts "Importing advisory data from #{year}"
|
||||||
jsons = read_jsons_for_year(year)
|
jsons = read_jsons_for_year(year)
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class GithubRepo
|
class GithubRepo
|
||||||
attr_accessor :repo_url, :repo_path
|
attr_accessor :repo_url, :repo_path
|
||||||
|
|
||||||
def initialize(repo_url=nil, repo_path=nil)
|
def initialize(repo_url = nil, repo_path = nil)
|
||||||
@repo_url = repo_url
|
@repo_url = repo_url
|
||||||
@repo_path = repo_path
|
@repo_path = repo_path
|
||||||
end
|
end
|
||||||
|
|
||||||
def git_clone_repo
|
def git_clone_repo
|
||||||
if repo_url.nil? || repo_path.nil?
|
if repo_url.nil? || repo_path.nil?
|
||||||
puts "Please provide a repo url and repo_path"
|
puts 'Please provide a repo url and repo_path'
|
||||||
else
|
else
|
||||||
Git.clone(repo_url, repo_path)
|
Git.clone(repo_url, repo_path)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
require '/data_importer/lib/github_api/user.rb'
|
# frozen_string_literal: true
|
||||||
require '/data_importer/lib/github_api/owner_repos.rb'
|
|
||||||
|
require '/data_importer/lib/github_api/user'
|
||||||
|
require '/data_importer/lib/github_api/owner_repos'
|
||||||
|
|
||||||
class GithubUserImporter
|
class GithubUserImporter
|
||||||
attr_accessor :filepath, :usernames
|
attr_accessor :filepath, :usernames
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
@filepath = '/data_importer/data/github_usernames.txt'
|
@filepath = '/data_importer/data/github_usernames.txt'
|
||||||
@usernames = File.read(filepath).split("\n")
|
@usernames = File.read(filepath).split("\n")
|
||||||
|
@ -10,7 +13,7 @@ class GithubUserImporter
|
||||||
|
|
||||||
def username_hashes
|
def username_hashes
|
||||||
usernames.map do |username|
|
usernames.map do |username|
|
||||||
Retryable.retryable(tries: 3, on: QueryExecutionError, sleep: lambda { |n| 4**n } ) do
|
Retryable.retryable(tries: 3, on: QueryExecutionError, sleep: ->(n) { 4**n }) do
|
||||||
username_response = GithubApi::User.find(username)
|
username_response = GithubApi::User.find(username)
|
||||||
username_repos = GithubApi::OwnerRepos.find(username)
|
username_repos = GithubApi::OwnerRepos.find(username)
|
||||||
repos_hash = { 'repositories' => username_repos }
|
repos_hash = { 'repositories' => username_repos }
|
||||||
|
@ -43,10 +46,10 @@ class GithubUserImporter
|
||||||
|
|
||||||
def import
|
def import
|
||||||
if filepath.nil?
|
if filepath.nil?
|
||||||
puts "Please provide a filepath in the projects data dir named github_usernames.txt with one username per line."
|
puts 'Please provide a filepath in the projects data dir named github_usernames.txt with one username per line.'
|
||||||
else
|
else
|
||||||
puts "Now importing GithubUsers"
|
puts 'Now importing GithubUsers'
|
||||||
usernames = username_hashes.map {|h| h['login'] }
|
usernames = username_hashes.map { |h| h['login'] }
|
||||||
puts "Now importing data from the following usernames: #{usernames}"
|
puts "Now importing data from the following usernames: #{usernames}"
|
||||||
bulk_insert(username_hashes)
|
bulk_insert(username_hashes)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,17 +1,20 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'bulk_insert'
|
require 'bulk_insert'
|
||||||
require 'json'
|
require 'json'
|
||||||
|
|
||||||
class InthewildCveExploitImporter
|
class InthewildCveExploitImporter
|
||||||
attr_accessor :url
|
attr_accessor :url
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
@url = 'https://inthewild.io/api/exploited'
|
@url = 'https://inthewild.io/api/exploited'
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_exploit_feed
|
def get_exploit_feed
|
||||||
r = RestClient::Request.execute(
|
r = RestClient::Request.execute(
|
||||||
:method => :get,
|
method: :get,
|
||||||
:url => url,
|
url: url,
|
||||||
:headers => {"Content-type": "application/json"}
|
headers: { "Content-type": 'application/json' }
|
||||||
)
|
)
|
||||||
if r.code == 200
|
if r.code == 200
|
||||||
JSON.parse(r.body)
|
JSON.parse(r.body)
|
||||||
|
@ -37,7 +40,7 @@ class InthewildCveExploitImporter
|
||||||
|
|
||||||
def import
|
def import
|
||||||
feed = get_exploit_feed
|
feed = get_exploit_feed
|
||||||
puts "Now importing InthewildCveExploits."
|
puts 'Now importing InthewildCveExploits.'
|
||||||
cves = feed.map do |cve_entry|
|
cves = feed.map do |cve_entry|
|
||||||
cve_attrs_from_item(cve_entry)
|
cve_attrs_from_item(cve_entry)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,19 +1,21 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'git'
|
require 'git'
|
||||||
require 'json'
|
require 'json'
|
||||||
require 'date'
|
require 'date'
|
||||||
require 'bulk_insert'
|
require 'bulk_insert'
|
||||||
require '/data_importer/lib/importers/github_repo.rb'
|
require '/data_importer/lib/importers/github_repo'
|
||||||
|
|
||||||
class PocInGithubImporter < GithubRepo
|
class PocInGithubImporter < GithubRepo
|
||||||
CVE_MATCHER = /(CVE|cve)-\d{4}-\d{4,7}/
|
CVE_MATCHER = /(CVE|cve)-\d{4}-\d{4,7}/.freeze
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
super(repo_url='https://github.com/nomi-sec/PoC-in-GitHub.git', repo_path='/data_importer/data/poc_in_github')
|
super(repo_url = 'https://github.com/nomi-sec/PoC-in-GitHub.git', repo_path = '/data_importer/data/poc_in_github')
|
||||||
end
|
end
|
||||||
|
|
||||||
# all the files are named CVE-year-1234.json in this repo
|
# all the files are named CVE-year-1234.json in this repo
|
||||||
def cve_from_filename(filename)
|
def cve_from_filename(filename)
|
||||||
File.basename(filename,File.extname(filename))
|
File.basename(filename, File.extname(filename))
|
||||||
end
|
end
|
||||||
|
|
||||||
# regex extract substring thats a cve-id from either the name or full_name json entries
|
# regex extract substring thats a cve-id from either the name or full_name json entries
|
||||||
|
@ -22,10 +24,9 @@ class PocInGithubImporter < GithubRepo
|
||||||
fullname = json['full_name']
|
fullname = json['full_name']
|
||||||
description = json['description']
|
description = json['description']
|
||||||
id = name.match(CVE_MATCHER)[0] || fullname.match(CVE_MATCHER)[0] || description.match(CVE_MATCHER)[0]
|
id = name.match(CVE_MATCHER)[0] || fullname.match(CVE_MATCHER)[0] || description.match(CVE_MATCHER)[0]
|
||||||
debug_hash = {:name => name, :fullname => fullname, :description => description, :id => id.upcase }
|
debug_hash = { name: name, fullname: fullname, description: description, id: id.upcase }
|
||||||
puts debug_hash
|
puts debug_hash
|
||||||
cve_id = id.upcase
|
id.upcase
|
||||||
cve_id
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_jsons_for_year(year)
|
def list_jsons_for_year(year)
|
||||||
|
@ -36,7 +37,7 @@ class PocInGithubImporter < GithubRepo
|
||||||
def read_jsons_for_year(year)
|
def read_jsons_for_year(year)
|
||||||
filenames = list_jsons_for_year(year)
|
filenames = list_jsons_for_year(year)
|
||||||
filenames.map do |f|
|
filenames.map do |f|
|
||||||
{:cve_id => cve_from_filename(f), :file_data => read_json(f) }
|
{ cve_id: cve_from_filename(f), file_data: read_json(f) }
|
||||||
end.flatten
|
end.flatten
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -46,7 +47,7 @@ class PocInGithubImporter < GithubRepo
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def cve_attrs_from_item(json, cve_attrs={})
|
def cve_attrs_from_item(json, cve_attrs = {})
|
||||||
cve_attrs[:github_poc_id] = json['id']
|
cve_attrs[:github_poc_id] = json['id']
|
||||||
cve_attrs[:name] = json['name']
|
cve_attrs[:name] = json['name']
|
||||||
cve_attrs[:full_name] = json['full_name']
|
cve_attrs[:full_name] = json['full_name']
|
||||||
|
@ -79,7 +80,7 @@ class PocInGithubImporter < GithubRepo
|
||||||
json_fd = info_hash[:file_data]
|
json_fd = info_hash[:file_data]
|
||||||
|
|
||||||
json_fd.map do |entry|
|
json_fd.map do |entry|
|
||||||
cve_attrs_from_item(entry, cve_attrs={:cve_id => cve_id})
|
cve_attrs_from_item(entry, cve_attrs = { cve_id: cve_id })
|
||||||
end
|
end
|
||||||
end.flatten
|
end.flatten
|
||||||
end
|
end
|
||||||
|
@ -92,7 +93,7 @@ class PocInGithubImporter < GithubRepo
|
||||||
cves_from_json = cves_for_year(year)
|
cves_from_json = cves_for_year(year)
|
||||||
|
|
||||||
ids = cves_from_json.map { |cve| cve[:github_poc_id] }
|
ids = cves_from_json.map { |cve| cve[:github_poc_id] }
|
||||||
ids_in_db = GithubPoc.where(:github_poc_id => ids).pluck(:github_poc_id)
|
ids_in_db = GithubPoc.where(github_poc_id: ids).pluck(:github_poc_id)
|
||||||
|
|
||||||
new_ids = ids - ids_in_db
|
new_ids = ids - ids_in_db
|
||||||
new_cves = cves_from_json.select { |cve| cve if new_ids.include?(cve[:github_poc_id]) }
|
new_cves = cves_from_json.select { |cve| cve if new_ids.include?(cve[:github_poc_id]) }
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'git'
|
require 'git'
|
||||||
require 'json'
|
require 'json'
|
||||||
require 'date'
|
require 'date'
|
||||||
|
@ -39,16 +41,16 @@ class TrickestPocCveImporter
|
||||||
data_hash = {}
|
data_hash = {}
|
||||||
doc = Nokogiri::HTML5.parse(html)
|
doc = Nokogiri::HTML5.parse(html)
|
||||||
h3_nodes = doc.xpath('//h3')
|
h3_nodes = doc.xpath('//h3')
|
||||||
h3_keys = doc.xpath('//h3').map {|n| n.children.first.text}
|
h3_keys = doc.xpath('//h3').map { |n| n.children.first.text }
|
||||||
h4_keys = doc.xpath('//h4').map {|n| n.children.first.text}
|
h4_keys = doc.xpath('//h4').map { |n| n.children.first.text }
|
||||||
data_hash_keys = (h3_keys + h4_keys).flatten
|
data_hash_keys = (h3_keys + h4_keys).flatten
|
||||||
|
|
||||||
# cve id is always the first url in the markdown doc
|
# cve id is always the first url in the markdown doc
|
||||||
cve_url = doc.xpath("//h3/a").attribute('href').value
|
cve_url = doc.xpath('//h3/a').attribute('href').value
|
||||||
cve_id = h3_keys.first
|
cve_id = h3_keys.first
|
||||||
|
|
||||||
p_text = doc.xpath('//p').map {|p| p.text }
|
p_text = doc.xpath('//p').map(&:text)
|
||||||
links_for_poc = doc.xpath('//p/a').map {|a| a.values}.flatten
|
links_for_poc = doc.xpath('//p/a').map(&:values).flatten
|
||||||
|
|
||||||
data_hash['cve_id'] = cve_id
|
data_hash['cve_id'] = cve_id
|
||||||
data_hash['cve_url'] = cve_url
|
data_hash['cve_url'] = cve_url
|
||||||
|
@ -106,7 +108,7 @@ class TrickestPocCveImporter
|
||||||
cves_from_markdown = cves_for_year(year)
|
cves_from_markdown = cves_for_year(year)
|
||||||
|
|
||||||
ids = cves_from_markdown.map { |cve| cve[:cve_id] }
|
ids = cves_from_markdown.map { |cve| cve[:cve_id] }
|
||||||
cve_ids_in_db = TrickestPocCve.where(:cve_id => ids).pluck(:cve_id)
|
cve_ids_in_db = TrickestPocCve.where(cve_id: ids).pluck(:cve_id)
|
||||||
|
|
||||||
new_cve_ids = ids - cve_ids_in_db
|
new_cve_ids = ids - cve_ids_in_db
|
||||||
new_cves = cves_from_markdown.select { |cve| cve if new_cve_ids.include?(cve[:cve_id]) }
|
new_cves = cves_from_markdown.select { |cve| cve if new_cve_ids.include?(cve[:cve_id]) }
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class JsonHelper
|
class JsonHelper
|
||||||
|
|
||||||
|
|
||||||
def self.deep_transform_keys(json_hash)
|
def self.deep_transform_keys(json_hash)
|
||||||
if json_hash.is_a? Array
|
if json_hash.is_a? Array
|
||||||
json_hash.map {|jh| symbolize_names_snake_case(jh) }
|
json_hash.map { |jh| symbolize_names_snake_case(jh) }
|
||||||
else
|
else
|
||||||
symbolize_names_snake_case(json_hash)
|
symbolize_names_snake_case(json_hash)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.symbolize_names_snake_case(json_hash)
|
def self.symbolize_names_snake_case(json_hash)
|
||||||
json_hash.deep_transform_keys {|k| k.to_s.underscore.to_sym }
|
json_hash.deep_transform_keys { |k| k.to_s.underscore.to_sym }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class GithubUsernamesPopulate
|
class GithubUsernamesPopulate
|
||||||
def self.usernames_from_pocs
|
def self.usernames_from_pocs
|
||||||
GithubPoc.pluck(:owner).map {|h| h['login']}.sort.uniq
|
GithubPoc.pluck(:owner).map { |h| h['login'] }.sort.uniq
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.update_file
|
def self.update_file
|
||||||
fp = '/data_importer/data/github_usernames.txt'
|
fp = '/data_importer/data/github_usernames.txt'
|
||||||
File.open(fp, "w+") do |f|
|
File.open(fp, 'w+') do |f|
|
||||||
puts "Updating username file found at #{fp}"
|
puts "Updating username file found at #{fp}"
|
||||||
f.puts(usernames_from_pocs)
|
f.puts(usernames_from_pocs)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
require "test_helper"
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'test_helper'
|
||||||
|
|
||||||
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
|
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
|
||||||
driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
|
driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
require "test_helper"
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'test_helper'
|
||||||
|
|
||||||
class CpesControllerTest < ActionDispatch::IntegrationTest
|
class CpesControllerTest < ActionDispatch::IntegrationTest
|
||||||
# test "the truth" do
|
# test "the truth" do
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
require "test_helper"
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'test_helper'
|
||||||
|
|
||||||
class CvesControllerTest < ActionDispatch::IntegrationTest
|
class CvesControllerTest < ActionDispatch::IntegrationTest
|
||||||
# test "the truth" do
|
# test "the truth" do
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
ENV['RAILS_ENV'] ||= 'test'
|
ENV['RAILS_ENV'] ||= 'test'
|
||||||
require_relative '../config/environment'
|
require_relative '../config/environment'
|
||||||
require 'rails/test_help'
|
require 'rails/test_help'
|
||||||
|
|
||||||
class ActiveSupport::TestCase
|
module ActiveSupport
|
||||||
|
class TestCase
|
||||||
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
|
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
|
||||||
fixtures :all
|
fixtures :all
|
||||||
|
|
||||||
# Add more helper methods to be used by all tests here...
|
# Add more helper methods to be used by all tests here...
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Reference in a new issue