11/* eslint-disable @typescript-eslint/no-explicit-any */
22import { isPlainObject } from '../utils/validation' ;
33
4+ /**
5+ * Custom type handler for Sanitizer.
6+ *
7+ * Allows user to register their own formatters from external packages.
8+ */
9+ export interface SanitizerTypeHandler {
10+ /**
11+ * Checks if this handler should be applied to given value
12+ *
13+ * @returns `true`
14+ */
15+ check : ( target : any ) => boolean ;
16+
17+ /**
18+ * Formats the value into a sanitized representation
19+ */
20+ format : ( target : any ) => any ;
21+ }
22+
423/**
524 * This class provides methods for preparing data to sending to Hawk
625 * - trim long strings
7- * - represent html elements like <div ...> as "<div>" instead of "{}"
826 * - represent big objects as "<big object>"
927 * - represent class as <class SomeClass> or <instance of SomeClass>
1028 */
@@ -30,6 +48,13 @@ export class Sanitizer {
3048 */
3149 private static readonly maxArrayLength : number = 10 ;
3250
51+ /**
52+ * Custom type handlers registered via {@link registerHandler}.
53+ *
54+ * Checked in {@link sanitize} before built-in type checks.
55+ */
56+ private static readonly customHandlers : SanitizerTypeHandler [ ] = [ ] ;
57+
3358 /**
3459 * Check if passed variable is an object
3560 *
@@ -39,6 +64,17 @@ export class Sanitizer {
3964 return isPlainObject ( target ) ;
4065 }
4166
67+ /**
68+ * Register a custom type handler.
69+ * Handlers are checked before built-in type checks, in reverse registration order
70+ * (last registered = highest priority).
71+ *
72+ * @param handler - handler to register
73+ */
74+ public static registerHandler ( handler : SanitizerTypeHandler ) : void {
75+ Sanitizer . customHandlers . unshift ( handler ) ;
76+ }
77+
4278 /**
4379 * Apply sanitizing for array/object/primitives
4480 *
@@ -62,19 +98,21 @@ export class Sanitizer {
6298 */
6399 if ( Sanitizer . isArray ( data ) ) {
64100 return this . sanitizeArray ( data , depth + 1 , seen ) ;
101+ }
65102
66- /**
67- * If value is an Element, format it as string with outer HTML
68- * HTMLDivElement -> "<div ...></div>"
69- */
70- } else if ( Sanitizer . isElement ( data ) ) {
71- return Sanitizer . formatElement ( data ) ;
103+ // Check additional handlers provided by env-specific modules or users
104+ // to sanitize some additional cases (e.g. specific object types)
105+ for ( const handler of Sanitizer . customHandlers ) {
106+ if ( handler . check ( data ) ) {
107+ return handler . format ( data ) ;
108+ }
109+ }
72110
73- /**
74- * If values is a not-constructed class, it will be formatted as "<class SomeClass>"
75- * class Editor {...} -> <class Editor>
76- */
77- } else if ( Sanitizer . isClassPrototype ( data ) ) {
111+ /**
112+ * If values is a not-constructed class, it will be formatted as "<class SomeClass>"
113+ * class Editor {...} -> <class Editor>
114+ */
115+ if ( Sanitizer . isClassPrototype ( data ) ) {
78116 return Sanitizer . formatClassPrototype ( data ) ;
79117
80118 /**
@@ -133,7 +171,9 @@ export class Sanitizer {
133171 * @param depth - current depth of recursion
134172 * @param seen - Set of already seen objects to prevent circular references
135173 */
136- private static sanitizeObject ( data : { [ key : string ] : any } , depth : number , seen : WeakSet < object > ) : Record < string , any > | '<deep object>' | '<big object>' {
174+ private static sanitizeObject ( data : {
175+ [ key : string ] : any
176+ } , depth : number , seen : WeakSet < object > ) : Record < string , any > | '<deep object>' | '<big object>' {
137177 /**
138178 * If the maximum depth is reached, return a placeholder
139179 */
@@ -207,24 +247,6 @@ export class Sanitizer {
207247 return typeof target === 'string' ;
208248 }
209249
210- /**
211- * Return string representation of the object type
212- *
213- * @param object - object to get type
214- */
215- private static typeOf ( object : any ) : string {
216- return Object . prototype . toString . call ( object ) . match ( / \s ( [ a - z A - Z ] + ) / ) [ 1 ] . toLowerCase ( ) ;
217- }
218-
219- /**
220- * Check if passed variable is an HTML Element
221- *
222- * @param target - variable to check
223- */
224- private static isElement ( target : any ) : boolean {
225- return target instanceof Element ;
226- }
227-
228250 /**
229251 * Return name of a passed class
230252 *
@@ -250,31 +272,12 @@ export class Sanitizer {
250272 */
251273 private static trimString ( target : string ) : string {
252274 if ( target . length > Sanitizer . maxStringLen ) {
253- return target . substr ( 0 , Sanitizer . maxStringLen ) + '…' ;
275+ return target . substring ( 0 , Sanitizer . maxStringLen ) + '…' ;
254276 }
255277
256278 return target ;
257279 }
258280
259- /**
260- * Represent HTML Element as string with it outer-html
261- * HTMLDivElement -> "<div ...></div>"
262- *
263- * @param target - variable to format
264- */
265- private static formatElement ( target : Element ) : string {
266- /**
267- * Also, remove inner HTML because it can be BIG
268- */
269- const innerHTML = target . innerHTML ;
270-
271- if ( innerHTML ) {
272- return target . outerHTML . replace ( target . innerHTML , '…' ) ;
273- }
274-
275- return target . outerHTML ;
276- }
277-
278281 /**
279282 * Represent not-constructed class as "<class SomeClass>"
280283 *
0 commit comments