From 42d980f4cb3b7362dab94747c6012298a87860f4 Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Wed, 20 Jul 2022 15:39:28 -0700 Subject: [PATCH] Add comment sanitization and markdown rendering --- Cargo.lock | 57 +++++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 1 + demo/a/index.html | 1 + demo/b/index.html | 1 + demo/soudan.js | 4 ++-- src/main.rs | 18 +++++++++++++-- 6 files changed, 76 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 04b4e16..7e36f7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -701,6 +701,20 @@ dependencies = [ "libc", ] +[[package]] +name = "html5ever" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5c13fb08e5d4dfc151ee5e88bae63f7773d61852f3bdc73c9f4b9e1bde03148" +dependencies = [ + "log", + "mac", + "markup5ever 0.10.1", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "html5ever" version = "0.26.0" @@ -709,7 +723,7 @@ checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" dependencies = [ "log", "mac", - "markup5ever", + "markup5ever 0.11.0", "proc-macro2", "quote", "syn", @@ -858,6 +872,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kuchiki" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ea8e9c6e031377cff82ee3001dc8026cdf431ed4e2e6b51f98ab8c73484a358" +dependencies = [ + "cssparser", + "html5ever 0.25.2", + "matches", + "selectors", +] + [[package]] name = "language-tags" version = "0.3.2" @@ -929,6 +955,20 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" +[[package]] +name = "markup5ever" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a24f40fb03852d1cdd84330cddcaf98e9ec08a7b7768e952fad3b4cf048ec8fd" +dependencies = [ + "log", + "phf 0.8.0", + "phf_codegen 0.8.0", + "string_cache", + "string_cache_codegen", + "tendril", +] + [[package]] name = "markup5ever" version = "0.11.0" @@ -1496,6 +1536,18 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" +[[package]] +name = "sanitize_html" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cd7ea46188a5c6961a44b4c25ed280ea85a1e0b6a129f9bbd81bbf87261c52b" +dependencies = [ + "html5ever 0.25.2", + "kuchiki", + "lazy_static", + "regex", +] + [[package]] name = "schannel" version = "0.1.20" @@ -1521,7 +1573,7 @@ dependencies = [ "cssparser", "ego-tree", "getopts", - "html5ever", + "html5ever 0.26.0", "matches", "selectors", "smallvec", @@ -1688,6 +1740,7 @@ dependencies = [ "md5", "reqwest", "rusqlite", + "sanitize_html", "scraper", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index ffe89f2..c043dd7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,3 +21,4 @@ md5 = "0.7.0" chrono = { version = "0.4.19", features = ["serde"] } reqwest = "0.11.11" scraper = "0.13.0" +sanitize_html = "0.7.0" diff --git a/demo/a/index.html b/demo/a/index.html index 25d385a..d4486c4 100644 --- a/demo/a/index.html +++ b/demo/a/index.html @@ -6,4 +6,5 @@

This is Page A.

+ diff --git a/demo/b/index.html b/demo/b/index.html index ad2e0e0..944f2cc 100644 --- a/demo/b/index.html +++ b/demo/b/index.html @@ -6,4 +6,5 @@

This is Page B.

+ diff --git a/demo/soudan.js b/demo/soudan.js index d2102c9..20dc586 100644 --- a/demo/soudan.js +++ b/demo/soudan.js @@ -9,7 +9,7 @@ document.getElementById("soudan").innerHTML = `

Make a comment

Comments

`; -document.write(``); +const md = window.markdownit().disable("image"); const url = "http://127.0.0.1:8080"; const form = document.getElementById("soudan-comment-form"); const commentContainer = document.getElementById("soudan-comments"); @@ -52,7 +52,7 @@ function reloadComments() { html = "

No comments yet! Be the first to make one.

"; } else { comments.forEach(comment => { - html += `
${comment.author ? comment.author : "Anonymous"} commented ${moment(new Date(comment.timestamp * 1000)).fromNow()}:
${comment.text}
`; + html += `
${comment.author ? comment.author : "Anonymous"} commented ${moment(new Date(comment.timestamp * 1000)).fromNow()}:
${md.render(comment.text)}
`; }); } commentContainer.innerHTML = html; diff --git a/src/main.rs b/src/main.rs index b56be4f..5db64e5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,6 +14,7 @@ use std::{ sync::{Mutex, MutexGuard}, }; use validator::Validate; +use sanitize_html::{sanitize_str, rules::predefined::DEFAULT, errors::SanitizeError}; struct AppState { databases: HashMap>, @@ -66,8 +67,21 @@ async fn post_comment( ) -> impl Responder { match String::from_utf8(bytes.to_vec()) { Ok(text) => { - let PostCommentsRequest { url, comment } = match serde_json::from_str(&text) { - Ok(req) => req, + let PostCommentsRequest { url, comment } = match serde_json::from_str::(&text) { + Ok(mut req) => { + let mut sanitize_req = || -> Result<(), SanitizeError> { + req.comment.text = sanitize_str(&DEFAULT, &req.comment.text)? + .replace(">", ">"); // required for markdown quotes + if let Some(ref mut author) = req.comment.author { + *author = sanitize_str(&DEFAULT, &author)?; + } + Ok(()) + }; + if let Err(_) = sanitize_req() { + return HttpResponse::InternalServerError().reason("failed to sanitize request").finish(); + } + req + } Err(_) => return HttpResponse::BadRequest().reason("invalid request body").finish(), }; if comment.validate().is_err() {