Class: Dawn::KnowledgeBase

Inherits:
Object
  • Object
show all
Includes:
Singleton
Defined in:
lib/dawn/knowledge_base.rb

Overview

This is the YAML powered knowledge base

Dawnscanner KB will be a bunch of YAML file, stored in a hierachy of directories resembling security checks family. A digital signature will be also available to prevent KB tampering.

This class will be accountable for:

+ check for KB upgrade
+ fetching the KB file from the Internet
+ verifying the database signature
+ reading YAML file, creating the security check array

Another big change will be the MVC passed as constructor parameter, so only the checks regarding the particular app, will be loaded in the security check array. This should speed up BasicCheck internal routines.

Class usage will be very simple. After getting the singleton instance, you will load the KB content. The load method will be also responsible about all relevant checks.

Example

require “dawn/knowledge_base”

d = Dawn::KnowledgeBase.instance d.update if d.update? d.load

Last update: Mon Mar 22 05:08:55 PM CET 2021

Constant Summary collapse

GEM_CHECK =
:rubygem_check
DEPENDENCY_CHECK =
:dependency_check
UNSAFE_DEPENDENCY_CHECK =
:unsafe_dependency_check
PATTERN_MATCH_CHECK =
:pattern_match_check
RUBY_VERSION_CHECK =
:ruby_version_check
OS_CHECK =
:os_check
COMBO_CHECK =
:combo_check
CUSTOM_CHECK =
:custom_check
REMOTE_KB_URL_PREFIX =
"https://dawnscanner.org/data/"
FILES =
%w(kb.yaml bulletin.tar.gz generic_check.tar.gz owasp_ror_cheatsheet.tar.gz code_style.tar.gz code_quality.tar.gz owasp_top_10.tar.gz signatures.tar.gz)
VERSION =
"0.0.1"
@@enabled_checks =
[:generic_check, :code_quality, :bulletin, :code_style, :owasp_top_10]

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ KnowledgeBase

Returns a new instance of KnowledgeBase.



86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/dawn/knowledge_base.rb', line 86

def initialize(options={})
  if $logger.nil?
    require 'dawn/logger'
    $logger = Logger.new(STDOUT)
    $logger.helo "knowledge-base-experimental", Dawn::VERSION
  end
  @path = default_path
  @path = options[:path] if options[:path]
  FileUtils.mkdir_p(@path)

  @enabled_checks = @@enabled_checks

  debug_me "KB root path is #{@path}"
end

Instance Attribute Details

#descriptorObject (readonly)

Returns the value of attribute descriptor.



82
83
84
# File 'lib/dawn/knowledge_base.rb', line 82

def descriptor
  @descriptor
end

#errorObject (readonly)

Returns the value of attribute error.



84
85
86
# File 'lib/dawn/knowledge_base.rb', line 84

def error
  @error
end

#pathObject (readonly)

Returns the value of attribute path.



83
84
85
# File 'lib/dawn/knowledge_base.rb', line 83

def path
  @path
end

#security_checksObject (readonly)

Returns the value of attribute security_checks.



81
82
83
# File 'lib/dawn/knowledge_base.rb', line 81

def security_checks
  @security_checks
end

Class Method Details

.enabled_checks=(checks) ⇒ Object



101
102
103
# File 'lib/dawn/knowledge_base.rb', line 101

def self.enabled_checks= checks
  @@enabled_checks=checks
end

.kb_descriptorObject



153
154
155
# File 'lib/dawn/knowledge_base.rb', line 153

def self.kb_descriptor
  {:kb=>{:version=>VERSION, :revision=>Time.now.strftime("%Y%m%d"), :api=>Dawn::VERSION}}.to_yaml
end

.path=(path_name) ⇒ Object



110
111
112
# File 'lib/dawn/knowledge_base.rb', line 110

def self.path= path_name
  @path=path_name
end

Instance Method Details

#allObject



178
179
180
# File 'lib/dawn/knowledge_base.rb', line 178

def all
  @security_checks
end

#default_pathObject



105
106
107
108
# File 'lib/dawn/knowledge_base.rb', line 105

def default_path
  @path = File.join(Dir.home, 'dawnscanner', 'kb')
  return @path
end

#dump(verbose = false) ⇒ Object



248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/dawn/knowledge_base.rb', line 248

def dump(verbose=false)
  puts "Security checks currently supported:"
  i=0
  KnowledgeBase.instance.all.each do |check|
    i+=1
    if verbose
      puts "Name: #{check.name}\tCVSS: #{check.cvss_score}\tReleased: #{check.release_date}"
      puts "Description\n#{check.message}"
      puts "Remediation\n#{check.remediation}\n\n"
    else
      puts "#{check.name}"
    end
  end
  puts "-----\nTotal: #{i}"

end

#find(name) ⇒ Object



123
124
125
# File 'lib/dawn/knowledge_base.rb', line 123

def find(name)
  debug_me "I'm asked to find #{name}"
end

#is_packed?Boolean

Returns:

  • (Boolean)


114
115
116
# File 'lib/dawn/knowledge_base.rb', line 114

def is_packed?
  return __packed?
end

#is_valid?Boolean

Returns:

  • (Boolean)


118
119
120
# File 'lib/dawn/knowledge_base.rb', line 118

def is_valid?
  return __valid?
end

#load(lint = false) ⇒ Object

Load security checks from db/ folder.

Returns an array of security checks, matching the mvc to be reviewed and the enabled check list or an empty array if an error occured.



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/dawn/knowledge_base.rb', line 186

def load(lint=false)
  good    =0
  invalid =0

  @security_checks = []
  # $path = File.join(Dir.pwd, "db")

  unless __valid?
    @error = "An invalid library it has been found. Please use --recovery flag to force fresh install from dawnscanner.org"
    return []
  end

  unless __load?
    @error = "The library must be consumed with dawnscanner up to v#{@descriptor[:kb][:api]}. You are using dawnscanner v#{Dawn::VERSION}"
    return []
  end

  @enabled_checks.each do |d|

    dir = File.join(@path, d.to_s)

    # Please note that if we enter in this branch, it means someone
    # tampered the KB between the previous __valid? check and this point.
    # Of course this is a very rare situation, but we must handle it.
    unless Dir.exists?(dir)
      $logger.warn "Missing check directory #{dir}"
    else
      Dir.glob(dir+"/**/*.yml").each do |f|
        begin
          data = YAML.load_file(f, permitted_classes: [Dawn::Kb::UnsafeDependencyCheck,
                                                       Dawn::Kb::BasicCheck,
                                                       Dawn::Kb::ComboCheck,
                                                       Dawn::Kb::DependencyCheck,
                                                       Dawn::Kb::DeprecationCheck,
                                                       Dawn::Kb::OperatingSystemCheck,
                                                       Dawn::Kb::PatternMatchCheck,
                                                       Dawn::Kb::RubygemCheck,
                                                       Dawn::Kb::RubyVersionCheck,
                                                       Dawn::Kb::VersionCheck,
                                                       Date,
                                                       Symbol])
          @security_checks << data
          good+=1
          $logger.info("#{File.basename(f)} loaded") if lint
        rescue Exception => e
          $logger.error(e.message)
          invalid+=1
        end
      end
    end

    if lint
      $logger.info("#{invalid} invalid checks out of #{good+invalid}")
    end


  end

  debug_me "#{@security_checks.count}"
  return @security_checks
end

#unpackObject



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/dawn/knowledge_base.rb', line 127

def unpack
  # https://weblog.jamisbuck.org/2015/7/23/tar-gz-in-ruby.html
  FILES.each do |f|
    full_name = File.join(path,f)
    if File.file?(full_name) and File.extname(full_name).eql?('.gz')
      File.open(full_name, "rb") do |file|
        Zlib::GzipReader.wrap(file) do |gz|
          Gem::Package::TarReader.new(gz) do |tar|
            tar.each do |entry|
              if entry.file?
                FileUtils.mkdir_p(File.dirname(File.join(path, entry.full_name)))
                File.open(File.join(path, entry.full_name), "wb") do |f|
                  f.write(entry.read)
                end
                File.chmod(entry.header.mode, File.join(path,entry.full_name))
              end
            end
          end
        end
      end
    else
      $logger.warn("can't open " + f)
    end
  end
end

#update?Boolean

Returns:

  • (Boolean)


157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/dawn/knowledge_base.rb', line 157

def update?
  FileUtils.mkdir_p("tmp")
  begin
    response = Net::HTTP.get URI(REMOTE_KB_URL_PREFIX + "kb.yaml")
    open("tmp/kb.yaml", "w") do |f|
      f.puts(response)
    end
    response = Net::HTTP.get URI(REMOTE_KB_URL_PREFIX + "kb.yaml.sig")
    open("tmp/kb.yaml.sig", "w") do |f|
      f.puts(response)
    end
  rescue Exception => e
    $logger.error e.to_s
    return false
  end

  # Verify kb.yaml signature

  YAML.load(response)
end