@@ -178,14 +178,19 @@ class _UnsafeUrlFilter extends html_parsing.TreeVisitor {
178178 void visitElement (html.Element element) {
179179 super .visitElement (element);
180180
181- final isUnsafe =
182- _isUnsafe (element, 'a ' , 'href' ) || _isUnsafe (element, 'img' , 'src' );
181+ final isUnsafe = _isUnsafe (element, 'a' , 'href' ) ||
182+ _isUnsafe (element, 'img ' , 'src' , allowDataBase64 : true );
183183 if (isUnsafe) {
184184 element.replaceWith (html.Text (element.text));
185185 }
186186 }
187187
188- bool _isUnsafe (html.Element element, String tag, String attr) {
188+ bool _isUnsafe (
189+ html.Element element,
190+ String tag,
191+ String attr, {
192+ bool allowDataBase64 = false ,
193+ }) {
189194 if (element.localName != tag) {
190195 return false ;
191196 }
@@ -194,14 +199,30 @@ class _UnsafeUrlFilter extends html_parsing.TreeVisitor {
194199 return false ;
195200 }
196201 final uri = Uri .tryParse (url);
197- if (uri == null || uri.isInvalid) {
202+ var isInvalid = uri == null || uri.isInvalid;
203+ if (allowDataBase64 &&
204+ uri != null &&
205+ uri.scheme == 'data' &&
206+ _isValidDataBase64ImageUrl (uri.path)) {
207+ isInvalid = false ;
208+ }
209+ if (isInvalid) {
198210 element.attributes.remove (attr);
199211 return true ;
200212 }
201213 return false ;
202214 }
203215}
204216
217+ bool _isValidDataBase64ImageUrl (String value) {
218+ // NOTE: This is only a high-level check. It doesn't decode the image bytes,
219+ // and it doesn't check if the content is valid and matching the specified type.
220+ if (RegExp (r'^image/[a-z0-9]+;base64,.*$' ).matchAsPrefix (value) == null ) {
221+ return false ;
222+ }
223+ return true ;
224+ }
225+
205226/// Rewrites relative URLs with the provided [urlResolverFn] .
206227class _RelativeUrlRewriter extends html_parsing.TreeVisitor {
207228 final UrlResolverFn ? urlResolverFn;
0 commit comments