diff --git a/app/controllers/cisa_known_exploits_controller.rb b/app/controllers/cisa_known_exploits_controller.rb new file mode 100644 index 0000000..cacbd55 --- /dev/null +++ b/app/controllers/cisa_known_exploits_controller.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class CisaKnownExploitsController < ApplicationController + def index + @cisa_known_exploits = CisaKnownExploit.all + render json: @cisa_known_exploits.to_json + end + + def show + @cisa_known_exploit = CisaKnownExploit.find(params[:cve_id]) + render json: @cisa_known_exploit.to_json + end +end diff --git a/app/models/cisa_known_exploit.rb b/app/models/cisa_known_exploit.rb new file mode 100644 index 0000000..48e85f7 --- /dev/null +++ b/app/models/cisa_known_exploit.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class CisaKnownExploit < ActiveRecord::Base + def self.find_by_id(id) + find_by(cve_id: id) + end + + def self.from_year(year) + where('cve_id LIKE ?', "CVE-#{year}-%") + end +end diff --git a/app/views/cisa_known_exploits/index.html.erb b/app/views/cisa_known_exploits/index.html.erb new file mode 100644 index 0000000..f2d4aac --- /dev/null +++ b/app/views/cisa_known_exploits/index.html.erb @@ -0,0 +1 @@ +

CisaKnownExploits#index

diff --git a/app/views/cisa_known_exploits/show.html.erb b/app/views/cisa_known_exploits/show.html.erb new file mode 100644 index 0000000..e69de29 diff --git a/app/workers/cisa_known_exploit_importer_worker.rb b/app/workers/cisa_known_exploit_importer_worker.rb new file mode 100644 index 0000000..f7ec7fc --- /dev/null +++ b/app/workers/cisa_known_exploit_importer_worker.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require '/data_importer/lib/importers/cisa_known_exploit_importer' + +class CisaKnownExploitImporterWorker + include Faktory::Job + + def perform(*args) + puts "Hello, I am #{jid} with args #{args}" + CisaKnownExploitImporter.new.import + end +end diff --git a/crontab.yaml b/crontab.yaml index 0656b18..d99dcda 100644 --- a/crontab.yaml +++ b/crontab.yaml @@ -59,4 +59,10 @@ jobs: schedule: "@every 4h00m00s" retries: 1 queue: default + priority: 5 + - job: CisaKnownExploitImporterWorker + args: [] + schedule: "@every 6h00m00s" + retries: 1 + queue: default priority: 5 \ No newline at end of file diff --git a/db/migrate/20220427043126_create_cisa_known_exploits.rb b/db/migrate/20220427043126_create_cisa_known_exploits.rb new file mode 100644 index 0000000..19618d2 --- /dev/null +++ b/db/migrate/20220427043126_create_cisa_known_exploits.rb @@ -0,0 +1,13 @@ +class CreateCisaKnownExploits < ActiveRecord::Migration[7.0] + def change + create_table :cisa_known_exploits do |t| + t.string :title + t.string :catalog_version + t.date :date_released + t.index :date_released, unique: true + t.integer :count + t.jsonb :vulnerabilities + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 3305715..523946e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,10 +10,21 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2022_04_19_203353) do +ActiveRecord::Schema[7.0].define(version: 2022_04_27_043126) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" + create_table "cisa_known_exploits", force: :cascade do |t| + t.string "title" + t.string "catalog_version" + t.date "date_released" + t.integer "count" + t.jsonb "vulnerabilities" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["date_released"], name: "index_cisa_known_exploits_on_date_released", unique: true + end + create_table "cnas", force: :cascade do |t| t.string "short_name" t.string "cna_id" diff --git a/db/seeds.rb b/db/seeds.rb index 3f2dad8..ab6f60f 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -18,6 +18,7 @@ require '/data_importer/lib/importers/cna_importer' require '/data_importer/lib/importers/github_advisory_importer' require '/data_importer/lib/importers/github_user_importer' require '/data_importer/lib/importers/gsd_importer.rb' +require '/data_importer/lib/importers/cisa_known_exploit_importer.rb' def line_sep puts '----------' * 12 @@ -30,6 +31,7 @@ def perform import_trickest_poc_cves import_inthewild_cve_exploits import_cvemon_cves + import_cisa_known_exploits import_cpes import_cnas import_github_advisories @@ -71,6 +73,11 @@ def import_inthewild_cve_exploits InthewildCveExploitImporter.new.import end +def import_cisa_known_exploits + line_sep + CisaKnownExploitImporter.new.import +end + def import_trickest_poc_cves line_sep TrickestPocCveImporter.new.import diff --git a/lib/importers/cisa_known_exploit_importer.rb b/lib/importers/cisa_known_exploit_importer.rb new file mode 100644 index 0000000..068d25b --- /dev/null +++ b/lib/importers/cisa_known_exploit_importer.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require '/data_importer/lib/json_helper' + +class CisaKnownExploitImporter + attr_accessor :feed_url + + EXPECTED_KEYS = %i[ + title + catalog_version + date_released + count + vulnerabilities + ].freeze + + EMPTY_HASH = EXPECTED_KEYS.map { |k| [k, nil] }.to_h.freeze + + def initialize + @feed_url = 'https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json' + end + + def get_and_transform_json + json = JsonHelper.read_json_from_url(feed_url) + json_transformed = JsonHelper.deep_transform_keys(json) + #json_transformed.map { |h| h.slice(*EXPECTED_KEYS).reverse_merge(EMPTY_HASH) } + end + + + def import + puts "Now starting import Cisa Known Exploits for #{feed_url}." + puts '----------' * 12 + cisa_known_exploits = [ get_and_transform_json ] + CisaKnownExploit.upsert_all(cisa_known_exploits, unique_by: :date_released) + end +end diff --git a/lib/json_helper.rb b/lib/json_helper.rb index 014b1f1..7bf4d5b 100644 --- a/lib/json_helper.rb +++ b/lib/json_helper.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true +require 'rest-client' class JsonHelper def self.deep_transform_keys(json_hash) @@ -17,4 +18,19 @@ class JsonHelper s.gsub("`\u0000`", "null_byte") end + def self.read_json_from_file(filename) + JSON.parse(File.read(filename), symbolize_names: true) + end + + def self.read_json_from_url(url) + r = RestClient::Request.execute( + :method => :get, + :url => url + ) + if r.code == 200 + JSON.parse(r.body, symobilize_names: true) + else + puts "Http Code: #{r.code}" + end + end end