@@ -153,6 +153,18 @@ function hookDomPurifyHrefAndSrcSanitizer(allowedLinkProtocols: readonly string[
153153 return toDisposable ( ( ) => dompurify . removeHook ( 'afterSanitizeAttributes' ) ) ;
154154}
155155
156+ /**
157+ * Predicate that checks if an attribute should be kept or removed.
158+ *
159+ * @returns A boolean indicating whether the attribute should be kept or a string with the sanitized value (which implicitly keeps the attribute)
160+ */
161+ export type SanitizeAttributePredicate = ( node : Element , data : { readonly attrName : string ; readonly attrValue : string } ) => boolean | string ;
162+
163+ export interface SanitizeAttributeRule {
164+ readonly attributeName : string ;
165+ shouldKeep : SanitizeAttributePredicate ;
166+ }
167+
156168export interface DomSanitizerConfig {
157169 /**
158170 * Configured the allowed html tags.
@@ -166,8 +178,8 @@ export interface DomSanitizerConfig {
166178 * Configured the allowed html attributes.
167179 */
168180 readonly allowedAttributes ?: {
169- readonly override ?: readonly string [ ] ;
170- readonly augment ?: readonly string [ ] ;
181+ readonly override ?: ReadonlyArray < string | SanitizeAttributeRule > ;
182+ readonly augment ?: ReadonlyArray < string | SanitizeAttributeRule > ;
171183 } ;
172184
173185 /**
@@ -190,11 +202,6 @@ export interface DomSanitizerConfig {
190202 * For example, <p><bad>"text"</bad></p> becomes <p>"<bad>text</bad>"</p>.
191203 */
192204 readonly replaceWithPlaintext ?: boolean ;
193-
194- // TODO: move these into more controlled api
195- readonly _do_not_use_hooks ?: {
196- readonly uponSanitizeAttribute ?: UponSanitizeAttributeCb ;
197- } ;
198205}
199206
200207const defaultDomPurifyConfig = Object . freeze ( {
@@ -230,16 +237,27 @@ export function sanitizeHtml(untrusted: string, config?: DomSanitizerConfig): Tr
230237 }
231238 }
232239
240+ let resolvedAttributes : Array < string | SanitizeAttributeRule > = [ ...defaultAllowedAttrs ] ;
233241 if ( config ?. allowedAttributes ) {
234242 if ( config . allowedAttributes . override ) {
235- resolvedConfig . ALLOWED_ATTR = [ ...config . allowedAttributes . override ] ;
243+ resolvedAttributes = [ ...config . allowedAttributes . override ] ;
236244 }
237245
238246 if ( config . allowedAttributes . augment ) {
239- resolvedConfig . ALLOWED_ATTR = [ ...( resolvedConfig . ALLOWED_ATTR ?? [ ] ) , ...config . allowedAttributes . augment ] ;
247+ resolvedAttributes = [ ...resolvedAttributes , ...config . allowedAttributes . augment ] ;
248+ }
249+ }
250+
251+ const allowedAttrNames = new Set ( resolvedAttributes . map ( attr => typeof attr === 'string' ? attr : attr . attributeName ) ) ;
252+ const allowedAttrPredicates = new Map < string , SanitizeAttributeRule > ( ) ;
253+ for ( const attr of resolvedAttributes ) {
254+ if ( typeof attr !== 'string' ) {
255+ allowedAttrPredicates . set ( attr . attributeName , attr ) ;
240256 }
241257 }
242258
259+ resolvedConfig . ALLOWED_ATTR = Array . from ( allowedAttrNames ) ;
260+
243261 store . add ( hookDomPurifyHrefAndSrcSanitizer (
244262 config ?. allowedLinkProtocols ?. override ?? [ Schemas . http , Schemas . https ] ,
245263 config ?. allowedMediaProtocols ?. override ?? [ Schemas . http , Schemas . https ] ) ) ;
@@ -248,8 +266,21 @@ export function sanitizeHtml(untrusted: string, config?: DomSanitizerConfig): Tr
248266 store . add ( addDompurifyHook ( 'uponSanitizeElement' , replaceWithPlainTextHook ) ) ;
249267 }
250268
251- if ( config ?. _do_not_use_hooks ?. uponSanitizeAttribute ) {
252- store . add ( addDompurifyHook ( 'uponSanitizeAttribute' , config . _do_not_use_hooks . uponSanitizeAttribute ) ) ;
269+ if ( allowedAttrPredicates . size ) {
270+ store . add ( addDompurifyHook ( 'uponSanitizeAttribute' , ( node , e ) => {
271+ const predicate = allowedAttrPredicates . get ( e . attrName ) ;
272+ if ( predicate ) {
273+ const result = predicate . shouldKeep ( node , e ) ;
274+ if ( typeof result === 'string' ) {
275+ e . keepAttr = true ;
276+ e . attrValue = result ;
277+ } else {
278+ e . keepAttr = result ;
279+ }
280+ } else {
281+ e . keepAttr = allowedAttrNames . has ( e . attrName ) ;
282+ }
283+ } ) ) ;
253284 }
254285
255286 return dompurify . sanitize ( untrusted , {
0 commit comments