diff --git a/CHANGELOG b/CHANGELOG
index c1da384b0f1957434fd5dd24eb64477c1b16c806..3394a6bdf05803e0f2f16495643f383d603b508c 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -4,6 +4,7 @@ v 8.13.0 (unreleased)
   - Speed-up group milestones show page
 
 v 8.12.1 (unreleased)
+  - Fix a memory leak in HTML::Pipeline::SanitizationFilter::WHITELIST
 
 v 8.12.0
   - Update the rouge gem to 2.0.6, which adds highlighting support for JSX, Prometheus, and others. !6251
diff --git a/lib/banzai/filter/sanitization_filter.rb b/lib/banzai/filter/sanitization_filter.rb
index ca80aac5a0894c810f5a0852cc43d05a125d9b65..2470362e01975c67d259f22e047332c60516d36f 100644
--- a/lib/banzai/filter/sanitization_filter.rb
+++ b/lib/banzai/filter/sanitization_filter.rb
@@ -43,55 +43,57 @@ module Banzai
         whitelist[:protocols].delete('a')
 
         # ...but then remove links with unsafe protocols
-        whitelist[:transformers].push(remove_unsafe_links)
+        whitelist[:transformers].push(self.class.remove_unsafe_links)
 
         # Remove `rel` attribute from `a` elements
-        whitelist[:transformers].push(remove_rel)
+        whitelist[:transformers].push(self.class.remove_rel)
 
         # Remove `class` attribute from non-highlight spans
-        whitelist[:transformers].push(clean_spans)
+        whitelist[:transformers].push(self.class.clean_spans)
 
         whitelist
       end
 
-      def remove_unsafe_links
-        lambda do |env|
-          node = env[:node]
+      class << self
+        def remove_unsafe_links
+          lambda do |env|
+            node = env[:node]
 
-          return unless node.name == 'a'
-          return unless node.has_attribute?('href')
+            return unless node.name == 'a'
+            return unless node.has_attribute?('href')
 
-          begin
-            uri = Addressable::URI.parse(node['href'])
-            uri.scheme = uri.scheme.strip.downcase if uri.scheme
+            begin
+              uri = Addressable::URI.parse(node['href'])
+              uri.scheme = uri.scheme.strip.downcase if uri.scheme
 
-            node.remove_attribute('href') if UNSAFE_PROTOCOLS.include?(uri.scheme)
-          rescue Addressable::URI::InvalidURIError
-            node.remove_attribute('href')
+              node.remove_attribute('href') if UNSAFE_PROTOCOLS.include?(uri.scheme)
+            rescue Addressable::URI::InvalidURIError
+              node.remove_attribute('href')
+            end
           end
         end
-      end
 
-      def remove_rel
-        lambda do |env|
-          if env[:node_name] == 'a'
-            env[:node].remove_attribute('rel')
+        def remove_rel
+          lambda do |env|
+            if env[:node_name] == 'a'
+              env[:node].remove_attribute('rel')
+            end
           end
         end
-      end
 
-      def clean_spans
-        lambda do |env|
-          node = env[:node]
+        def clean_spans
+          lambda do |env|
+            node = env[:node]
 
-          return unless node.name == 'span'
-          return unless node.has_attribute?('class')
+            return unless node.name == 'span'
+            return unless node.has_attribute?('class')
 
-          unless has_ancestor?(node, 'pre')
-            node.remove_attribute('class')
-          end
+            unless node.ancestors.any? { |n| n.name.casecmp('pre').zero? }
+              node.remove_attribute('class')
+            end
 
-          { node_whitelist: [node] }
+            { node_whitelist: [node] }
+          end
         end
       end
     end