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[
  metadata.json wp_fingerprints.json
  timthumbs-v3.txt config_backups.txt db_exports.txt
  dynamic_finders.yml LICENSE sponsor.txt
].freeze
OLD_FILES =
%w[
  wordpress.db user-agents.txt dynamic_finders_01.yml
  wordpresses.json plugins.json themes.json
].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(repo_directory) ⇒ Updater

Returns a new instance of Updater.



22
23
24
25
26
27
28
29
30
# File 'lib/wpscan/db/updater.rb', line 22

def initialize(repo_directory)
  @repo_directory = Pathname.new(repo_directory).expand_path

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

  raise "#{repo_directory} is not writable" unless repo_directory.writable?

  delete_old_files
end

Instance Attribute Details

#repo_directoryObject (readonly)

Returns the value of attribute repo_directory.



20
21
22
# File 'lib/wpscan/db/updater.rb', line 20

def repo_directory
  @repo_directory
end

Instance Method Details

#backup_file_path(filename) ⇒ String

Returns:

  • (String)


104
105
106
# File 'lib/wpscan/db/updater.rb', line 104

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

#create_backup(filename) ⇒ Object



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

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



120
121
122
# File 'lib/wpscan/db/updater.rb', line 120

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



34
35
36
37
38
# File 'lib/wpscan/db/updater.rb', line 34

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:



125
126
127
128
129
130
131
132
133
134
135
# File 'lib/wpscan/db/updater.rb', line 125

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

  res = Browser.get(file_url, request_params)
  raise Error::Download, 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)


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

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)


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

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

#local_file_checksum(filename) ⇒ Object



99
100
101
# File 'lib/wpscan/db/updater.rb', line 99

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

#local_file_path(filename) ⇒ String

Returns:

  • (String)


95
96
97
# File 'lib/wpscan/db/updater.rb', line 95

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

#missing_files?Boolean

Returns:

  • (Boolean)


60
61
62
63
64
65
# File 'lib/wpscan/db/updater.rb', line 60

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

#outdated?Boolean

Returns:

  • (Boolean)


53
54
55
56
57
# File 'lib/wpscan/db/updater.rb', line 53

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:



85
86
87
88
89
90
91
92
# File 'lib/wpscan/db/updater.rb', line 85

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

  res = Browser.get(url, request_params)
  raise Error::Download, 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



80
81
82
# File 'lib/wpscan/db/updater.rb', line 80

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

#request_paramsHash

Note:

Those params can’t be overriden by CLI options

Returns The params for Typhoeus::Request.

Returns:

  • (Hash)

    The params for Typhoeus::Request



69
70
71
72
73
74
75
76
77
# File 'lib/wpscan/db/updater.rb', line 69

def request_params
  @request_params ||= {
    timeout: 600,
    connecttimeout: 300,
    accept_encoding: 'gzip, deflate',
    cache_ttl: 0,
    headers: { 'User-Agent' => Browser.instance.default_user_agent, 'Referer' => nil }
  }
end

#restore_backup(filename) ⇒ Object



114
115
116
117
118
# File 'lib/wpscan/db/updater.rb', line 114

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



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/wpscan/db/updater.rb', line 138

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