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
31
32
33
34
35
36
# 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)

  # When --no-update is passed, return to avoid raising an error if the directory is not writable
  # Mainly there for Homebrew: https://github.com/wpscanteam/wpscan/pull/1455
  return if ParsedCli.update == false

  unless repo_directory.writable?
    raise "#{repo_directory} is not writable (uid: #{Process.uid}, gid: #{Process.gid})"
  end

  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)


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

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

#create_backup(filename) ⇒ Object



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

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



126
127
128
# File 'lib/wpscan/db/updater.rb', line 126

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



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

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:



131
132
133
134
135
136
137
138
139
140
141
# File 'lib/wpscan/db/updater.rb', line 131

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

  res = Typhoeus.get(file_url, request_params)
  raise Error::Download, res if res.timed_out? || res.code != 200

  File.binwrite(file_path, res.body)

  local_file_checksum(filename)
end

#last_updateTime?

Returns:

  • (Time, nil)


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

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)


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

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

#local_file_checksum(filename) ⇒ Object



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

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

#local_file_path(filename) ⇒ String

Returns:

  • (String)


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

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

#missing_files?Boolean

Returns:

  • (Boolean)


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

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

#outdated?Boolean

Returns:

  • (Boolean)


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

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:



91
92
93
94
95
96
97
98
# File 'lib/wpscan/db/updater.rb', line 91

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

  res = Typhoeus.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



86
87
88
# File 'lib/wpscan/db/updater.rb', line 86

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



75
76
77
78
79
80
81
82
83
# File 'lib/wpscan/db/updater.rb', line 75

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

#restore_backup(filename) ⇒ Object



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

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



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/wpscan/db/updater.rb', line 144

def update
  updated = []

  FILES.each do |filename|
    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 Error::ChecksumsMismatch, filename 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

  File.write(last_update_file, Time.now)

  updated
end