aboutsummaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
authorMartin Fischer <martin@push-f.com>2021-07-03 11:50:29 +0200
committerMartin Fischer <martin@push-f.com>2021-07-03 20:23:22 +0200
commit306289dfd1b55b24eee3195ae56a6db7539b6740 (patch)
treea142988d2910ee5d096bcc6db12595c866f03905 /src/main.rs
parentf50225041545ecf71ead3e493203f16f4b5f24c0 (diff)
render .html files securely using IFrames
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs51
1 files changed, 38 insertions, 13 deletions
diff --git a/src/main.rs b/src/main.rs
index a5b7ce0..58431a2 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -265,17 +265,18 @@ async fn service<C: Controller>(
});
// we rely on CSP to thwart XSS attacks, all modern browsers support it
- resp.headers_mut().insert(
- header::CONTENT_SECURITY_POLICY,
- format!(
- "default-src 'self'; frame-src {}; script-src {}; style-src {}",
- frame_csp,
- script_csp,
- include_str!("static/style.css.sha"),
- )
- .parse()
- .unwrap(),
- );
+ resp.headers_mut()
+ .entry(header::CONTENT_SECURITY_POLICY)
+ .or_insert_with(|| {
+ format!(
+ "default-src 'self'; frame-src {}; script-src {}; style-src {}",
+ frame_csp,
+ script_csp,
+ include_str!("static/style.css.sha"),
+ )
+ .parse()
+ .unwrap()
+ });
Ok(resp)
}
@@ -526,16 +527,40 @@ impl Context {
}
}
-fn render_markdown(input: &str, page: &mut Page) {
+#[derive(PartialEq)]
+enum RenderMode {
+ View,
+ Preview,
+}
+
+fn render_markdown(input: &str, page: &mut Page, _mode: RenderMode) {
let parser = Parser::new_ext(input, Options::all());
page.body.push_str("<div class=markdown-output>");
html::push_html(&mut page.body, parser);
page.body.push_str("</div>");
}
-fn get_renderer(path: &Path) -> Option<fn(&str, &mut Page)> {
+fn embed_html_as_iframe(input: &str, page: &mut Page, mode: RenderMode) {
+ if mode == RenderMode::View {
+ page.body.push_str("<iframe src='?action=run'></iframe>");
+ page.frame_src = Some("'self'");
+ } else {
+ page.body
+ .push_str("<div class=note>Note that JavaScript does not work in the preview.</div>");
+ // sandbox=allow-scripts wouldn't work because the strict parent page CSP still applies
+
+ // The sandbox attribute makes browsers treat the embedded page as a unique origin.
+ page.body.push_str(&format!(
+ "<iframe srcdoc='{}' sandbox></iframe>",
+ html_escape(input)
+ ));
+ }
+}
+
+fn get_renderer(path: &Path) -> Option<fn(&str, &mut Page, RenderMode)> {
match path.extension().map(|e| e.to_str().unwrap()) {
Some("md") => Some(render_markdown),
+ Some("html") => Some(embed_html_as_iframe),
_ => None,
}
}