11import  logging 
2+ from  collections .abc  import  Callable 
23from  typing  import  Any 
34
45from  django .conf  import  settings 
6+ from  django .utils .module_loading  import  import_string 
57
68logger  =  logging .getLogger (__name__ )
79
810
9- def  get_nh3_default_options () ->  dict [str , Any ]:
11+ def  get_nh3_configured_default_options () ->  dict [str , Any ]:
1012    """ 
1113    Pull the django-nh3 settings similarly to how django-bleach handled them. 
1214
@@ -16,35 +18,177 @@ def get_nh3_default_options() -> dict[str, Any]:
1618        BLEACH_ALLOWED_TAGS         -> NH3_ALLOWED_TAGS 
1719        BLEACH_ALLOWED_ATTRIBUTES   -> NH3_ALLOWED_ATTRIBUTES 
1820        BLEACH_STRIP_COMMENTS       -> NH3_STRIP_COMMENTS 
21+         BLEACH_ALLOWED_PROTOCOLS    -> NH3_ALLOWED_URL_SCHEMES 
1922
2023    While other settings have no current support in nh3: 
2124
2225        BLEACH_ALLOWED_STYLES       -> There is no support for styling 
23-         BLEACH_ALLOWED_PROTOCOLS    -> There is no support for protocols 
2426        BLEACH_STRIP_TAGS           -> This is the default behavior of nh3 
2527
2628    """ 
27-     nh3_args : dict [str , Any ] =  {}
2829
2930    nh3_settings  =  {
31+         # Sets the tags that are allowed (eg: allowlist) 
32+         # Ensure that no tags in this are also in NH3_CLEAN_CONTENT_TAGS or 
33+         # NH3_ALLOWED_ATTRIBUTES 
3034        "NH3_ALLOWED_TAGS" : "tags" ,
35+         # Sets the tags whose contents will be completely removed from the 
36+         # output (eg: blocklist) 
37+         # Ensure that no tags in this are also in NH3_ALLOWED_TAGS or 
38+         # NH3_ALLOWED_ATTRIBUTES 
39+         # Default: script, style 
3140        "NH3_ALLOWED_ATTRIBUTES" : "attributes" ,
41+         # Sets the HTML attributes that are allowed on specific tags, * key 
42+         # means the attributes are allowed on any tag (eg: allowlist) 
43+         # Ensure that no tags in this are also in NH3_CLEAN_CONTENT_TAGS 
44+         "NH3_CLEAN_CONTENT_TAGS" : "clean_content_tags" ,
45+         # Dotted path to a callback that allows rewriting of all attributes. 
46+         # The callback takes name of the element, attribute and its value. 
47+         # Returns None to remove the attribute, or a value to use 
48+         "NH3_ALLOWED_ATTRIBUTES_FILTER" : "attribute_filter" ,
49+         # Configures the handling of HTML comments, defaults to True 
3250        "NH3_STRIP_COMMENTS" : "strip_comments" ,
51+         # Configures a rel attribute that will be added on links, defaults to 
52+         # noopener noreferrer. To turn on rel-insertion, pass a space-separated 
53+         # list. If rel is in the generic or tag attributes, this must be set to 
54+         # None 
55+         # Common rel values to include: 
56+         # noopener 
57+         # noreferrer 
58+         # nofollow 
59+         "NH3_LINK_REL" : "link_rel" ,
60+         # Sets the prefix of attributes that are allowed on any tag 
61+         "NH3_ALLOWED_GENERIC_ATTRIBUTE_PREFIXES" : "generic_attribute_prefixes" ,
62+         # Sets the values of HTML attributes that are allowed on specific tags. 
63+         # The value is structured as a map from tag names to a map from 
64+         # attribute names to a set of attribute values. If a tag is not itself 
65+         # whitelisted, adding entries to this map will do nothing. 
66+         "NH3_ALLOWED_TAG_ATTRIBUTE_VALUES" : "tag_attribute_values" ,
67+         # Sets the values of HTML attributes that are to be set on specific 
68+         # tags. The value is structured as a map from tag names to a map from 
69+         # attribute names to an attribute value. If a tag is not itself 
70+         # whitelisted, adding entries to this map will do nothing. 
71+         "NH3_SET_TAG_ATTRIBUTE_VALUES" : "set_tag_attribute_values" ,
72+         # Sets the URL schemes permitted on href and src attributes 
73+         "NH3_ALLOWED_URL_SCHEMES" : "url_schemes" ,
3374    }
3475
35-     for  setting , kwarg  in  nh3_settings .items ():
36-         if  hasattr (settings , setting ):
37-             attr  =  getattr (settings , setting )
76+     return  {
77+         kwarg : getattr (settings , setting_name )
78+         for  setting_name , kwarg  in  nh3_settings .items ()
79+         if  hasattr (settings , setting_name )
80+     }
81+ 
82+ 
83+ def  normalize_nh3_options (  # noqa: C901, PLR0912 
84+     options : dict [str , Any ],
85+ ) ->  dict [str , Any ]:
86+     nh3_args : dict [str , Any ] =  {}
87+     for  kwarg_name , kwarg_value  in  options .items ():
88+         value  =  kwarg_value 
89+ 
90+         # Convert from general iterables to sets 
91+         if  kwarg_name  in  [
92+             "tags" ,
93+             "clean_content_tags" ,
94+             "generic_attribute_prefixes" ,
95+             "url_schemes" ,
96+         ]:
97+             value  =  set (value )
98+ 
99+         elif  kwarg_name  ==  "attributes" :
100+             copy_dict  =  value .copy ()
101+             for  tag , attributes  in  value .items ():
102+                 copy_dict [tag ] =  set (attributes )
103+             value  =  copy_dict 
104+ 
105+         elif  kwarg_name  ==  "attribute_filter" :
106+             if  callable (value ):
107+                 pass 
108+             elif  isinstance (value , str ):
109+                 value  =  import_string (value )
110+ 
111+         elif  kwarg_name  ==  "strip_comments" :
112+             value  =  bool (value )
38113
39-             # Convert from general iterables to sets 
40-             if  setting  ==  "NH3_ALLOWED_TAGS" :
41-                 attr  =  set (attr )
42-             elif  setting  ==  "NH3_ALLOWED_ATTRIBUTES" :
43-                 copy_dict  =  attr .copy ()
44-                 for  tag , attributes  in  attr .items ():
45-                     copy_dict [tag ] =  set (attributes )
46-                 attr  =  copy_dict 
114+         elif  kwarg_name  ==  "link_rel" :
115+             value  =  str (value )
47116
48-             nh3_args [kwarg ] =  attr 
117+         elif  kwarg_name  ==  "tag_attribute_values" :
118+             # The value is structured as a map from tag names to a map from 
119+             # attribute names to a set of attribute values. 
120+             allowed_tag_attr_dict : dict [str , dict [str , set [str ]]] =  {}
121+             for  tag_name , attribute_dict  in  value .items ():
122+                 allowed_tag_attr_dict [tag_name ] =  {}
123+                 for  attr_name , attr_value  in  attribute_dict .items ():
124+                     allowed_tag_attr_dict [tag_name ][attr_name ] =  set (attr_value )
125+             value  =  allowed_tag_attr_dict 
126+ 
127+         elif  kwarg_name  ==  "set_tag_attribute_values" :
128+             # The value is structured as a map from tag names to a map from 
129+             # attribute names to an attribute value. 
130+             set_tag_attr_dict : dict [str , dict [str , str ]] =  {}
131+             for  tag_name , attribute_dict  in  value .items ():
132+                 set_tag_attr_dict [tag_name ] =  {}
133+                 for  attr_name , attr_value  in  attribute_dict .items ():
134+                     set_tag_attr_dict [tag_name ][attr_name ] =  str (attr_value )
135+             value  =  set_tag_attr_dict 
136+ 
137+         nh3_args [kwarg_name ] =  value 
49138
50139    return  nh3_args 
140+ 
141+ 
142+ def  get_nh3_default_options () ->  dict [str , Any ]:
143+     return  normalize_nh3_options (get_nh3_configured_default_options ())
144+ 
145+ 
146+ def  get_nh3_options (
147+     tags : set [str ] |  None  =  None ,
148+     clean_content_tags : set [str ] |  None  =  None ,
149+     attributes : dict [str , set [str ]] |  None  =  None ,
150+     attribute_filter : Callable [[str , str , str ], str ] |  None  =  None ,
151+     strip_comments : bool  =  False ,
152+     link_rel : str  =  "" ,
153+     generic_attribute_prefixes : set [str ] |  None  =  None ,
154+     tag_attribute_values : dict [str , dict [str , set [str ]]] |  None  =  None ,
155+     set_tag_attribute_values : dict [str , dict [str , str ]] |  None  =  None ,
156+     url_schemes : set [str ] |  None  =  None ,
157+ ) ->  dict [str , Any ]:
158+     defaults  =  get_nh3_configured_default_options ()
159+ 
160+     tags  =  tags  or  defaults .get ("tags" , None ) or  set ()
161+     attributes  =  attributes  or  defaults .get ("attributes" , {})
162+     clean_content_tags  =  (
163+         clean_content_tags  or  defaults .get ("clean_content_tags" , None ) or  set ()
164+     )
165+     attribute_filter  =  attribute_filter  or  defaults .get ("attribute_filter" , None )
166+     strip_comments  =  strip_comments  or  defaults .get ("strip_comments" , False )
167+     link_rel  =  link_rel  or  defaults .get ("link_rel" , "" )
168+     generic_attribute_prefixes  =  (
169+         generic_attribute_prefixes 
170+         or  defaults .get ("generic_attribute_prefixes" , None )
171+         or  set ()
172+     )
173+     tag_attribute_values  =  (
174+         tag_attribute_values  or  defaults .get ("tag_attribute_values" , None ) or  {}
175+     )
176+     set_tag_attribute_values  =  (
177+         set_tag_attribute_values  or  defaults .get ("set_tag_attribute_values" , None ) or  {}
178+     )
179+     url_schemes  =  url_schemes  or  defaults .get ("url_schemes" , None ) or  set ()
180+ 
181+     return  normalize_nh3_options (
182+         {
183+             "tags" : tags ,
184+             "clean_content_tags" : clean_content_tags ,
185+             "attributes" : attributes ,
186+             "attribute_filter" : attribute_filter ,
187+             "strip_comments" : strip_comments ,
188+             "link_rel" : link_rel ,
189+             "generic_attribute_prefixes" : generic_attribute_prefixes ,
190+             "tag_attribute_values" : tag_attribute_values ,
191+             "set_tag_attribute_values" : set_tag_attribute_values ,
192+             "url_schemes" : url_schemes ,
193+         }
194+     )
0 commit comments