Skip to content

Commit 45efc70

Browse files
committed
Allowing same data URLs for images.
1 parent 1b9ce31 commit 45efc70

File tree

2 files changed

+47
-4
lines changed

2 files changed

+47
-4
lines changed

app/lib/shared/markdown.dart

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -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].
206227
class _RelativeUrlRewriter extends html_parsing.TreeVisitor {
207228
final UrlResolverFn? urlResolverFn;

app/test/shared/markdown_test.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,4 +448,26 @@ void main() {
448448
]);
449449
});
450450
});
451+
452+
group('data URLs', () {
453+
final dataUrl =
454+
'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==';
455+
456+
test('rejected in links', () {
457+
final output = markdownToHtml('[link]($dataUrl)');
458+
expect(output, '<p>link</p>\n');
459+
});
460+
461+
test('accepted in image', () {
462+
// TODO: also enable data: URLs in sanitize_html
463+
final output = markdownToHtml('![link]($dataUrl)');
464+
expect(output, '<p><img alt="link"></p>\n');
465+
});
466+
467+
test('rejected non-image', () {
468+
final output =
469+
markdownToHtml('![link](data:text/plain;base64,invalid-content)');
470+
expect(output, '<p></p>\n');
471+
});
472+
});
451473
}

0 commit comments

Comments
 (0)