From 76a7f614652fdb5fef5951e9e54e7e3b9f4afbf1 Mon Sep 17 00:00:00 2001 From: bcoles Date: Mon, 20 Apr 2026 22:17:40 +1000 Subject: [PATCH 1/2] Deduplicate notes hash keys and values in metadata Obj Notes keys ("Stability", "SideEffects", "Reliability") and values ("crash-safe", "ioc-in-logs", etc.) are repeated across thousands of modules. Use frozen string dedup (-str) to share a single object per unique string, reducing ~24K string allocations to ~185 shared objects. --- lib/msf/core/modules/metadata/obj.rb | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/msf/core/modules/metadata/obj.rb b/lib/msf/core/modules/metadata/obj.rb index 46a66ff195..3ed2ae3e60 100644 --- a/lib/msf/core/modules/metadata/obj.rb +++ b/lib/msf/core/modules/metadata/obj.rb @@ -36,6 +36,22 @@ class Obj @platform_list_cache[platform_string] ||= build_platform_list(platform_string) end + # Deduplicate notes hash keys and string values via the frozen string table. + # Keys like "Stability", "SideEffects", "Reliability" repeat across thousands + # of modules; values like "crash-safe", "ioc-in-logs" repeat hundreds of times. + def dedup_notes(notes) + notes.each_with_object({}) do |(k, v), h| + h[-k] = case v + when Array + v.map { |e| e.is_a?(String) ? -e : e } + when String + -v + else + v + end + end + end + private def build_platform_list(platform_string) @@ -295,7 +311,7 @@ class Obj @post_auth = obj_hash['post_auth'] @default_credential = obj_hash['default_credential'] notes = obj_hash['notes'] - @notes = (notes.nil? || notes.empty?) ? EMPTY_HASH : notes + @notes = (notes.nil? || notes.empty?) ? EMPTY_HASH : Obj.dedup_notes(notes) @needs_cleanup = obj_hash['needs_cleanup'] @session_types = obj_hash['session_types'] @autofilter_ports = obj_hash['autofilter_ports'] From 09bb98d13e4f3293db65af75f8c953c10177b4e0 Mon Sep 17 00:00:00 2001 From: bcoles Date: Mon, 20 Apr 2026 22:19:55 +1000 Subject: [PATCH 2/2] Memoize Obj#path to avoid repeated File.join The install_root path is immutable at runtime, so cache the computed full path on first access instead of calling File.join on every call. --- lib/msf/core/modules/metadata/obj.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/modules/metadata/obj.rb b/lib/msf/core/modules/metadata/obj.rb index 3ed2ae3e60..aaea792589 100644 --- a/lib/msf/core/modules/metadata/obj.rb +++ b/lib/msf/core/modules/metadata/obj.rb @@ -273,7 +273,7 @@ class Obj def path if @is_install_path - return ::File.join(Msf::Config.install_root, @path) + return @full_path ||= ::File.join(Msf::Config.install_root, @path) end @path