diff --git a/README.md b/README.md index 31b20670..c07dfbda 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ FLAGS: --github-pre-lang Use GitHub-style
for code blocks
--hardbreaks Treat newlines as hard line breaks
-h, --help Prints help information
+ --safe Suppress raw HTML and dangerous URLs
--smart Use smart punctuation
-V, --version Prints version information
@@ -113,6 +114,8 @@ assert_eq!(
As with [`cmark-gfm`](https://github.com/github/cmark#security), Comrak will pass through inline HTML, dangerous links, anything you can imagine — it only performs Markdown to HTML conversion per the CommonMark/GFM spec. We recommend the use of a sanitisation library like [`ammonia`](https://github.com/notriddle/ammonia) configured specific to your needs.
+You can also disable this potentially unsafe feature by using the `safe` option (or `--safe` at the command-line).
+
## Extensions
Comrak supports the five extensions to CommonMark defined in the
diff --git a/src/html.rs b/src/html.rs
index 35fae74d..db7b1f4b 100644
--- a/src/html.rs
+++ b/src/html.rs
@@ -2,6 +2,7 @@ use ctype::isspace;
use nodes::{AstNode, ListType, NodeValue, TableAlignment};
use parser::ComrakOptions;
use regex::Regex;
+use scanners;
use std::borrow::Cow;
use std::cell::Cell;
use std::collections::HashSet;
@@ -149,6 +150,10 @@ fn tagfilter_block(input: &[u8], o: &mut Write) -> io::Result<()> {
Ok(())
}
+fn dangerous_url(input: &[u8]) -> bool {
+ scanners::dangerous_url(input).is_some()
+}
+
impl<'o> HtmlFormatter<'o> {
fn new(options: &'o ComrakOptions, output: &'o mut WriteWithLast<'o>) -> Self {
HtmlFormatter {
@@ -416,7 +421,9 @@ impl<'o> HtmlFormatter<'o> {
},
NodeValue::HtmlBlock(ref nhb) => if entering {
try!(self.cr());
- if self.options.ext_tagfilter {
+ if self.options.safe {
+ try!(self.output.write_all(b""));
+ } else if self.options.ext_tagfilter {
try!(tagfilter_block(&nhb.literal, &mut self.output));
} else {
try!(self.output.write_all(&nhb.literal));
@@ -472,7 +479,9 @@ impl<'o> HtmlFormatter<'o> {
try!(self.output.write_all(b""));
},
NodeValue::HtmlInline(ref literal) => if entering {
- if self.options.ext_tagfilter && tagfilter(literal) {
+ if self.options.safe {
+ try!(self.output.write_all(b""));
+ } else if self.options.ext_tagfilter && tagfilter(literal) {
try!(self.output.write_all(b"<"));
try!(self.output.write_all(&literal[1..]));
} else {
@@ -501,7 +510,9 @@ impl<'o> HtmlFormatter<'o> {
},
NodeValue::Link(ref nl) => if entering {
try!(self.output.write_all(b" HtmlFormatter<'o> {
},
NodeValue::Image(ref nl) => if entering {
try!(self.output.write_all(b"
,
+ /// Disable rendering of raw HTML and potentially dangerous links.
+ ///
+ /// ```
+ /// # use comrak::{markdown_to_html, ComrakOptions};
+ /// let mut options = ComrakOptions::default();
+ /// let input = "\n\n\
+ /// Possibly .\n\n\
+ /// [Dangerous](javascript:alert(document.cookie)).\n\n\
+ /// [Safe](http://commonmark.org).\n";
+ ///
+ /// assert_eq!(markdown_to_html(input, &options),
+ /// "\n\
+ /// Possibly .
\n\
+ /// \n\
+ /// Safe.
\n");
+ ///
+ /// options.safe = true;
+ /// assert_eq!(markdown_to_html(input, &options),
+ /// "\n\
+ /// Possibly annoying.
\n\
+ /// \n\
+ /// Safe.
\n");
+ /// ```
+ pub safe: bool,
+
/// Enables the
/// [strikethrough extension](https://github.github.com/gfm/#strikethrough-extension-)
/// from the GFM spec.
diff --git a/src/scanners.rs b/src/scanners.rs
index 48982ec4..ef37976e 100644
--- a/src/scanners.rs
+++ b/src/scanners.rs
@@ -178,3 +178,8 @@ pub fn table_cell_end(line: &[u8]) -> Option {
pub fn table_row_end(line: &[u8]) -> Option {
search(Rule::table_row_end, line)
}
+
+#[inline(always)]
+pub fn dangerous_url(line: &[u8]) -> Option {
+ search(Rule::dangerous_url, line)
+}
diff --git a/src/tests.rs b/src/tests.rs
index 590919c7..c395ea75 100644
--- a/src/tests.rs
+++ b/src/tests.rs
@@ -821,3 +821,30 @@ fn cm_autolink_regression() {
// Testing that the cm renderer handles this case without crashing
html("", "\n");
}
+
+#[test]
+fn safe() {
+ html_opts(
+ concat!(
+ "[data:png](data:png/x)\n\n",
+ "[data:gif](data:gif/x)\n\n",
+ "[data:jpeg](data:jpeg/x)\n\n",
+ "[data:webp](data:webp/x)\n\n",
+ "[data:malicious](data:malicious/x)\n\n",
+ "[javascript:malicious](javascript:malicious)\n\n",
+ "[vbscript:malicious](vbscript:malicious)\n\n",
+ "[file:malicious](file:malicious)\n\n",
+ ),
+ concat!(
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ ),
+ |opts| opts.safe = true,
+ )
+}