Class: WPScan::DB::Updater

Inherits:
Object
  • Object
show all
Defined in:
lib/wpscan/db/updater.rb

Overview

Class used to perform DB updates :nocov:

Constant Summary collapse

FILES =

/!\ Might want to also update the Enumeration#cli_options when some filenames are changed here

%w[
  plugins.json themes.json wordpresses.json
  timthumbs-v3.txt user-agents.txt config_backups.txt
  db_exports.txt dynamic_finders.yml wp_fingerprints.json LICENSE
].freeze
OLD_FILES =
%w[wordpress.db dynamic_finders_01.yml].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(repo_directory) ⇒ Updater

Returns a new instance of Updater.



17
18
19
20
21
22
23
24
25
# File 'lib/wpscan/db/updater.rb', line 17

def initialize(repo_directory)
  @repo_directory = repo_directory

  FileUtils.mkdir_p(repo_directory) unless Dir.exist?(repo_directory)

  raise "#{repo_directory} is not writable" unless Pathname.new(repo_directory).writable?

  delete_old_files
end

Instance Attribute Details

#repo_directoryObject (readonly)

Returns the value of attribute repo_directory.



15
16
17
# File 'lib/wpscan/db/updater.rb', line 15

def repo_directory
  @repo_directory
end

Instance Method Details

#backup_file_path(filename) ⇒ Object



97
98
99
# File 'lib/wpscan/db/updater.rb', line 97

def backup_file_path(filename)
  File.join(repo_directory, "#{filename}.back")
end

#create_backup(filename) ⇒ Object



101
102
103
104
105
# File 'lib/wpscan/db/updater.rb', line 101

def create_backup(filename)
  return unless File.exist?(local_file_path(filename))

  FileUtils.cp(local_file_path(filename), backup_file_path(filename))
end

#delete_backup(filename) ⇒ Object



113
114
115
# File 'lib/wpscan/db/updater.rb', line 113

def delete_backup(filename)
  FileUtils.rm(backup_file_path(filename))
end

#delete_old_filesObject

Removes DB files which are no longer used this doesn’t raise errors if they don’t exist



29
30
31
32
33
# File 'lib/wpscan/db/updater.rb', line 29

def delete_old_files
  OLD_FILES.each do |old_file|
    FileUtils.remove_file(local_file_path(old_file), true)
  end
end

#download(filename) ⇒ String

Returns The checksum of the downloaded file.

Returns:

  • (String)

    The checksum of the downloaded file

Raises:



118
119
120
121
122
123
124
125
126
127
128
# File 'lib/wpscan/db/updater.rb', line 118

def download(filename)
  file_path = local_file_path(filename)
  file_url  = remote_file_url(filename)

  res = Browser.get(file_url, request_params)
  raise DownloadError, res if res.timed_out? || res.code != 200

  File.open(file_path, 'wb') { |f| f.write(res.body) }

  local_file_checksum(filename)
end

#last_updateTime?

Returns:

  • (Time, nil)


36
37
38
39
40
# File 'lib/wpscan/db/updater.rb', line 36

def last_update
  Time.parse(File.read(last_update_file))
rescue ArgumentError, Errno::ENOENT
  nil # returns nil if the file does not exist or contains invalid time data
end

#last_update_fileString

Returns:

  • (String)


43
44
45
# File 'lib/wpscan/db/updater.rb', line 43

def last_update_file
  @last_update_file ||= File.join(repo_directory, '.last_update')
end

#local_file_checksum(filename) ⇒ Object



93
94
95
# File 'lib/wpscan/db/updater.rb', line 93

def local_file_checksum(filename)
  Digest::SHA512.file(local_file_path(filename)).hexdigest
end

#local_file_path(filename) ⇒ Object



89
90
91
# File 'lib/wpscan/db/updater.rb', line 89

def local_file_path(filename)
  File.join(repo_directory, filename.to_s)
end

#missing_files?Boolean

Returns:

  • (Boolean)


55
56
57
58
59
60
# File 'lib/wpscan/db/updater.rb', line 55

def missing_files?
  FILES.each do |file|
    return true unless File.exist?(File.join(repo_directory, file))
  end
  false
end

#outdated?Boolean

Returns:

  • (Boolean)


48
49
50
51
52
# File 'lib/wpscan/db/updater.rb', line 48

def outdated?
  date = last_update

  date.nil? || date < 5.days.ago
end

#remote_file_checksum(filename) ⇒ String

Returns The checksum of the associated remote filename.

Returns:

  • (String)

    The checksum of the associated remote filename

Raises:



80
81
82
83
84
85
86
87
# File 'lib/wpscan/db/updater.rb', line 80

def remote_file_checksum(filename)
  url = "#{remote_file_url(filename)}.sha512"

  res = Browser.get(url, request_params)
  raise DownloadError, res if res.timed_out? || res.code != 200

  res.body.chomp
end

#remote_file_url(filename) ⇒ String

Returns The raw file URL associated with the given filename.

Returns:

  • (String)

    The raw file URL associated with the given filename



75
76
77
# File 'lib/wpscan/db/updater.rb', line 75

def remote_file_url(filename)
  "https://data.wpscan.org/#{filename}"
end

#request_paramsHash

Returns The params for Typhoeus::Request.

Returns:

  • (Hash)

    The params for Typhoeus::Request



63
64
65
66
67
68
69
70
71
72
# File 'lib/wpscan/db/updater.rb', line 63

def request_params
  {
    ssl_verifyhost: 2,
    ssl_verifypeer: true,
    timeout: 300,
    connecttimeout: 120,
    accept_encoding: 'gzip, deflate',
    cache_ttl: 0
  }
end

#restore_backup(filename) ⇒ Object



107
108
109
110
111
# File 'lib/wpscan/db/updater.rb', line 107

def restore_backup(filename)
  return unless File.exist?(backup_file_path(filename))

  FileUtils.cp(backup_file_path(filename), local_file_path(filename))
end

#updateArray<String>

Returns The filenames updated.

Returns:

  • (Array<String>)

    The filenames updated



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/wpscan/db/updater.rb', line 131

def update
  updated = []

  FILES.each do |filename|
    begin
      db_checksum = remote_file_checksum(filename)

      # Checking if the file needs to be updated
      next if File.exist?(local_file_path(filename)) && db_checksum == local_file_checksum(filename)

      create_backup(filename)
      dl_checksum = download(filename)

      raise "#{filename}: checksums do not match" unless dl_checksum == db_checksum

      updated << filename
    rescue StandardError => e
      restore_backup(filename)
      raise e
    ensure
      delete_backup(filename) if File.exist?(backup_file_path(filename))
    end
  end

  File.write(last_update_file, Time.now)

  updated
end