From e8f48ca9c105c4c7cb62bd0567df7a4318e935c8 Mon Sep 17 00:00:00 2001 From: Pavel Horal Date: Tue, 16 Jul 2024 22:35:36 +0200 Subject: [PATCH] Properly escape inline link and image attributes. Fix #473. --- src/commonmark-rules.js | 38 ++++++++++++++++++++++++++++++-------- test/index.html | 20 ++++++++++++++++++++ 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/src/commonmark-rules.js b/src/commonmark-rules.js index f32a8933..f9bcde54 100644 --- a/src/commonmark-rules.js +++ b/src/commonmark-rules.js @@ -152,11 +152,10 @@ rules.inlineLink = { }, replacement: function (content, node) { - var href = node.getAttribute('href') - if (href) href = href.replace(/([()])/g, '\\$1') - var title = cleanAttribute(node.getAttribute('title')) - if (title) title = ' "' + title.replace(/"/g, '\\"') + '"' - return '[' + content + '](' + href + title + ')' + var href = escapeLinkDestination(node.getAttribute('href')) + var title = escapeLinkTitle(cleanAttribute(node.getAttribute('title'))) + var titlePart = title ? ' "' + title + '"' : '' + return '[' + content + '](' + href + titlePart + ')' } } @@ -250,10 +249,10 @@ rules.image = { filter: 'img', replacement: function (content, node) { - var alt = cleanAttribute(node.getAttribute('alt')) - var src = node.getAttribute('src') || '' + var alt = escapeMarkdown(cleanAttribute(node.getAttribute('alt'))) + var src = escapeLinkDestination(node.getAttribute('src') || '') var title = cleanAttribute(node.getAttribute('title')) - var titlePart = title ? ' "' + title + '"' : '' + var titlePart = title ? ' "' + escapeLinkTitle(title) + '"' : '' return src ? '![' + alt + ']' + '(' + src + titlePart + ')' : '' } } @@ -262,4 +261,27 @@ function cleanAttribute (attribute) { return attribute ? attribute.replace(/(\n+\s*)+/g, '\n') : '' } +var ESCAPE_PATTERNS = { + before: /([\\*`[\]_]|(?:^[-+>])|(?:^~~~)|(?:^#{1-6}))/g, + after: /((?:^\d+(?=\.)))/ +} +var escapePattern = new RegExp( + '(?:' + ESCAPE_PATTERNS.before.source + '|' + ESCAPE_PATTERNS.after.source + ')', + 'g' +) + +function escapeMarkdown (content) { + return content.replace(escapePattern, function (match, before, after) { + return before ? '\\' + before : after + '\\' + }) +} + +function escapeLinkDestination (destination) { + return destination.replace(/([()])/g, '\\$1') +} + +function escapeLinkTitle (title) { + return title.replace(/"/g, '\\"') +} + export default rules diff --git a/test/index.html b/test/index.html index 065de26e..11c0bf95 100644 --- a/test/index.html +++ b/test/index.html @@ -171,11 +171,21 @@
![img with alt](logo.png)
+
+
_img_ *with* [alt]
+
![\_img\_ \*with\* \[alt\]](logo.png)
+
+

 
+
+
+
![](logo.png?\(query\))
+
+
img with
     alt
@@ -199,6 +209,11 @@ title")
+
+
+
![](logo.png "\"hello\"")
+
+
[An anchor](http://example.com)
@@ -237,6 +252,11 @@
[Some `code`](http://example.com/code)
+
+ +
[Some \[text\]](http://example.com/code)
+
+
[Reference link][1]