@@ -142,6 +142,9 @@ class BaseEvent:
142142 _discovery_context_regex = re .compile (r"\{(?:event|module)[^}]*\}" )
143143 # Stats class for the status line — override in subclasses for custom formatting
144144 _stats_class = None
145+ # Whether this event is subject to `url_extension_special` distribution filtering.
146+ # False for events representing already-retrieved content, whose consumers analyze the body.
147+ _url_special_filterable = True
145148
146149 # using __slots__ dramatically reduces memory usage in large scans
147150 __slots__ = [
@@ -1164,18 +1167,24 @@ def sanitize_data(self, data):
11641167class DictEvent (BaseEvent ):
11651168 __slots__ = ["url_extension" ]
11661169
1170+ def _set_url_extension (self ):
1171+ """Extract the file extension from self.parsed_url and record it as an attribute and a tag."""
1172+ parsed_url = getattr (self , "parsed_url" , None )
1173+ if parsed_url is None :
1174+ return
1175+ url_path = parsed_url .path
1176+ if not url_path :
1177+ return
1178+ extension = get_file_extension (str (url_path ).lower ())
1179+ if extension :
1180+ self .url_extension = extension
1181+ self .add_tag (f"extension-{ extension } " )
1182+
11671183 def sanitize_data (self , data ):
11681184 url = data .get ("url" , "" )
11691185 if url :
11701186 self .parsed_url = self .validators .validate_url_parsed (url )
1171- # extract url_extension from any dict event with a URL
1172- url_path = self .parsed_url .path
1173- if url_path :
1174- parsed_path_lower = str (url_path ).lower ()
1175- extension = get_file_extension (parsed_path_lower )
1176- if extension :
1177- self .url_extension = extension
1178- self .add_tag (f"extension-{ extension } " )
1187+ self ._set_url_extension ()
11791188 return data
11801189
11811190 def _data_load (self , data ):
@@ -1446,15 +1455,7 @@ def sanitize_data(self, data):
14461455 self .parsed_url = self .validators .validate_url_parsed (url )
14471456 data ["url" ] = self .parsed_url .geturl ()
14481457
1449- # special handling of URL extensions
1450- if self .parsed_url is not None :
1451- url_path = self .parsed_url .path
1452- if url_path :
1453- parsed_path_lower = str (url_path ).lower ()
1454- extension = get_file_extension (parsed_path_lower )
1455- if extension :
1456- self .url_extension = extension
1457- self .add_tag (f"extension-{ extension } " )
1458+ self ._set_url_extension ()
14581459
14591460 # tag as dir or endpoint
14601461 if str (self .parsed_url .path ).endswith ("/" ):
@@ -1716,6 +1717,10 @@ def _words(self):
17161717
17171718
17181719class HTTP_RESPONSE (URL_UNVERIFIED ):
1720+ # the response body has already been retrieved, so content-analysis modules
1721+ # should still receive it even for special extensions like .js
1722+ _url_special_filterable = False
1723+
17191724 def __init__ (self , * args , ** kwargs ):
17201725 super ().__init__ (* args , ** kwargs )
17211726 # count number of consecutive redirects
@@ -1776,6 +1781,7 @@ def sanitize_data(self, data):
17761781 url = data .get ("url" , "" )
17771782 self .parsed_url = self .validators .validate_url_parsed (url )
17781783 data ["url" ] = self .parsed_url .geturl ()
1784+ self ._set_url_extension ()
17791785
17801786 if not "raw_header" in data :
17811787 raise ValueError ("raw_header is required for HTTP_RESPONSE events" )
0 commit comments