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 config_backups.txt db_exports.txt
  dynamic_finders.yml wp_fingerprints.json LICENSE
].freeze
OLD_FILES =
%w[wordpress.db user-agents.txt 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.



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

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.



17
18
19
# File 'lib/wpscan/db/updater.rb', line 17

def repo_directory
  @repo_directory
end

Instance Method Details

#backup_file_path(filename) ⇒ String

Returns:

  • (String)


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

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

#create_backup(filename) ⇒ Object



105
106
107
108
109
# File 'lib/wpscan/db/updater.rb', line 105

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



117
118
119
# File 'lib/wpscan/db/updater.rb', line 117

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



31
32
33
34
35
# File 'lib/wpscan/db/updater.rb', line 31

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:



122
123
124
125
126
127
128
129
130
131
132
# File 'lib/wpscan/db/updater.rb', line 122

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)


38
39
40
41
42
# File 'lib/wpscan/db/updater.rb', line 38

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)


45
46
47
# File 'lib/wpscan/db/updater.rb', line 45

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

#local_file_checksum(filename) ⇒ Object



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

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

#local_file_path(filename) ⇒ String

Returns:

  • (String)


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

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

#missing_files?Boolean

Returns:

  • (Boolean)


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

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

#outdated?Boolean

Returns:

  • (Boolean)


50
51
52
53
54
# File 'lib/wpscan/db/updater.rb', line 50

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:



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

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



77
78
79
# File 'lib/wpscan/db/updater.rb', line 77

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



66
67
68
69
70
71
72
73
74
# File 'lib/wpscan/db/updater.rb', line 66

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

#restore_backup(filename) ⇒ Object



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

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



135
136
137
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
# File 'lib/wpscan/db/updater.rb', line 135

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