Module: Msf::Modules::Metadata::Store
- Included in:
- Cache
- Defined in:
- lib/msf/core/modules/metadata/store.rb
Overview
Handles storage of module metadata on disk. A base metadata file is always included - this was added to ensure a much better first time user experience as generating the user based metadata file requires 100+ mb at the time of creating this module. Subsequent starts of metasploit will load from a user specific metadata file as users potentially load modules from other places.
Constant Summary collapse
- BaseMetaDataFile =
'modules_metadata_base.json'- UserMetaDataFile =
'modules_metadata.json'- CacheMetaDataFile =
'module_metadata_cache.json'
Class Method Summary collapse
-
.calculate_file_checksums(files, cache) ⇒ Array<Array>
Calculate checksums for all files, using the cache when possible.
-
.calculate_overall_checksum(file_crc32s) ⇒ Integer
Calculate the overall checksum from individual file checksums.
-
.checksums_match?(current_checksum, cached_checksum) ⇒ Boolean
Compare the current checksum with the cached checksum.
-
.collect_files_to_check ⇒ Array<String>
Collect all files that need to be checked for checksums.
-
.get_cache_path ⇒ String
Get the path to the cache file.
-
.get_cached_checksum ⇒ Integer?
Get the cached checksum value from the combined cache file.
-
.get_current_checksum ⇒ Integer
Calculate the current checksum for all module and library files This calculates checksums for each file and generates an overall checksum from the individual file checksums.
-
.load_combined_cache(cache_file) ⇒ Hash
Load the combined cache from disk (contains both files and checksum).
-
.save_combined_cache(cache_file, files_cache, overall_checksum) ⇒ void
Save the combined cache to disk (files and checksum in one file).
-
.update_cache_checksum(current_checksum) ⇒ void
Update the cache with the current checksum and file data.
-
.valid_checksum? ⇒ Boolean
This method checks if the current module and library files match the cached checksum.
Instance Method Summary collapse
-
#init_store ⇒ Object
Initializes from user store (under ~/store/.msf4) if it exists.
- #initialize ⇒ Object
Class Method Details
.calculate_file_checksums(files, cache) ⇒ Array<Array>
Calculate checksums for all files, using the cache when possible
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/msf/core/modules/metadata/store.rb', line 197 def self.calculate_file_checksums(files, cache) Parallel.map(files, in_threads: Etc.nprocessors * 2) do |file| # Get file metadata (size and last modified time) = File.stat(file) cache_entry = cache[file] # Use cached CRC32 if mtime and size match, otherwise recalculate if cache_entry && cache_entry['mtime'] == .mtime.to_i && cache_entry['size'] == .size crc32 = cache_entry['crc32'] else crc32 = File.open(file, 'rb') { |fd| Zlib.crc32(fd.read, 0) } end # Return file and its metadata for later aggregation [file, { 'crc32' => crc32, 'mtime' => .mtime.to_i, 'size' => .size }] end end |
.calculate_overall_checksum(file_crc32s) ⇒ Integer
Calculate the overall checksum from individual file checksums
181 182 183 |
# File 'lib/msf/core/modules/metadata/store.rb', line 181 def self.calculate_overall_checksum(file_crc32s) Zlib.crc32(file_crc32s.join(','), 0) end |
.checksums_match?(current_checksum, cached_checksum) ⇒ Boolean
Compare the current checksum with the cached checksum
174 175 176 |
# File 'lib/msf/core/modules/metadata/store.rb', line 174 def self.checksums_match?(current_checksum, cached_checksum) current_checksum == cached_checksum end |
.collect_files_to_check ⇒ Array<String>
Collect all files that need to be checked for checksums.
187 188 189 190 191 |
# File 'lib/msf/core/modules/metadata/store.rb', line 187 def self.collect_files_to_check modules_dir = File.join(Msf::Config.install_root, 'modules', '**', '*') local_modules_dir = File.join(Msf::Config.user_module_directory, '**', '*') Dir.glob([modules_dir, local_modules_dir]).select { |f| File.file?(f) }.sort end |
.get_cache_path ⇒ String
Get the path to the cache file
219 220 221 |
# File 'lib/msf/core/modules/metadata/store.rb', line 219 def self.get_cache_path File.join(Msf::Config.config_directory, "store", CacheMetaDataFile) end |
.get_cached_checksum ⇒ Integer?
Get the cached checksum value from the combined cache file
258 259 260 261 262 |
# File 'lib/msf/core/modules/metadata/store.rb', line 258 def self.get_cached_checksum cache_path = get_cache_path cache_data = load_combined_cache(cache_path) cache_data['checksum'] end |
.get_current_checksum ⇒ Integer
Calculate the current checksum for all module and library files This calculates checksums for each file and generates an overall checksum from the individual file checksums. Does NOT update the cached checksum.
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/msf/core/modules/metadata/store.rb', line 153 def self.get_current_checksum files = collect_files_to_check cache_file = get_cache_path cache_data = load_combined_cache(cache_file) files_lookup = {} cache_data['files'].each { |entry| files_lookup[entry['path']] = entry } = calculate_file_checksums(files, files_lookup) file_crc32s = .map { |_, | ['crc32'] }.sort overall_checksum = calculate_overall_checksum(file_crc32s) overall_checksum end |
.load_combined_cache(cache_file) ⇒ Hash
Load the combined cache from disk (contains both files and checksum)
226 227 228 229 230 231 232 233 234 235 236 237 |
# File 'lib/msf/core/modules/metadata/store.rb', line 226 def self.load_combined_cache(cache_file) if File.exist?(cache_file) cache_content = JSON.parse(File.read(cache_file)) # Ensure the cache has the expected structure { 'files' => cache_content['files'] || [], 'checksum' => cache_content['checksum'] } else { 'files' => [], 'checksum' => nil } end end |
.save_combined_cache(cache_file, files_cache, overall_checksum) ⇒ void
This method returns an undefined value.
Save the combined cache to disk (files and checksum in one file)
244 245 246 247 248 249 250 251 252 253 254 |
# File 'lib/msf/core/modules/metadata/store.rb', line 244 def self.save_combined_cache(cache_file, files_cache, overall_checksum) # Ensure the directory for the cache file exists before writing FileUtils.mkdir_p(File.dirname(cache_file)) cache_content = { 'checksum' => overall_checksum, 'files' => files_cache } File.write(cache_file, JSON.pretty_generate(cache_content)) end |
.update_cache_checksum(current_checksum) ⇒ void
This method returns an undefined value.
Update the cache with the current checksum and file data
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 |
# File 'lib/msf/core/modules/metadata/store.rb', line 267 def self.update_cache_checksum(current_checksum) # Recalculate file checksums and update both overall checksum and file cache files = collect_files_to_check cache_file = get_cache_path cache_data = load_combined_cache(cache_file) files_lookup = {} cache_data['files'].each { |entry| files_lookup[entry['path']] = entry } = calculate_file_checksums(files, files_lookup) updated_files_cache = .map do |file_path, | .merge('path' => file_path) end updated_files_cache.sort_by! { |entry| entry['path'] } # Save both the updated file cache and the new overall checksum save_combined_cache(cache_file, updated_files_cache, current_checksum) end |
.valid_checksum? ⇒ Boolean
This method checks if the current module and library files match the cached checksum. It uses a per-file CRC32 cache to avoid recalculating checksums for files that haven't changed. If no cache exists, it will create one in the user's directory.
135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/msf/core/modules/metadata/store.rb', line 135 def self.valid_checksum? current_checksum = get_current_checksum cached_sha = get_cached_checksum # If no cached checksum exists, create the cache file with current checksum if cached_sha.nil? update_cache_checksum(current_checksum) return false end checksums_match?(current_checksum, cached_sha) end |
Instance Method Details
#init_store ⇒ Object
Initializes from user store (under ~/store/.msf4) if it exists. else base file (under $INSTALL_ROOT/db) is copied and loaded.
24 25 26 |
# File 'lib/msf/core/modules/metadata/store.rb', line 24 def init_store end |
#initialize ⇒ Object
13 14 15 |
# File 'lib/msf/core/modules/metadata/store.rb', line 13 def initialize @update_mutex = Mutex.new end |