diff --git a/Cargo.toml b/Cargo.toml
index 1261685..a7361f4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,6 +12,7 @@ license = "MIT/Apache-2.0"
[features]
sass = ["rsass"]
+sri = ["ssri"]
mime02 = []
mime03 = ["mime"]
warp02 = ["mime03"]
@@ -24,6 +25,7 @@ md5 = "0.7"
nom = "5.0.0"
rsass = { version = "0.13.0", optional = true }
+ssri = { version = "5.0.0", optional = true }
mime = { version = "0.3", optional = true }
[badges]
diff --git a/examples/actix/Cargo.toml b/examples/actix/Cargo.toml
index be896fc..db7bb98 100644
--- a/examples/actix/Cargo.toml
+++ b/examples/actix/Cargo.toml
@@ -7,7 +7,7 @@ edition = "2018"
build = "src/build.rs"
[build-dependencies]
-ructe = { path = "../..", features = ["sass", "mime03"] }
+ructe = { path = "../..", features = ["sass", "sri", "mime03"] }
[dependencies]
actix-web = "2.0.0"
diff --git a/examples/actix/templates/error.rs.html b/examples/actix/templates/error.rs.html
index 506741b..5b3e771 100644
--- a/examples/actix/templates/error.rs.html
+++ b/examples/actix/templates/error.rs.html
@@ -9,7 +9,7 @@
Error @code.as_u16(): @code.canonical_reason().unwrap_or("error")
-
+
diff --git a/examples/actix/templates/page.rs.html b/examples/actix/templates/page.rs.html
index c8ad365..8e61adb 100644
--- a/examples/actix/templates/page.rs.html
+++ b/examples/actix/templates/page.rs.html
@@ -8,7 +8,7 @@
Example
-
+
diff --git a/examples/ed2018/Cargo.toml b/examples/ed2018/Cargo.toml
index b13d41c..f225f00 100644
--- a/examples/ed2018/Cargo.toml
+++ b/examples/ed2018/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2018"
build = "src/build.rs"
[build-dependencies]
-ructe = { path = "../..", features = ["mime03", "sass"] }
+ructe = { path = "../..", features = ["mime03", "sri", "sass"] }
[dependencies]
mime = "~0.3"
diff --git a/examples/ed2018/templates/page.rs.html b/examples/ed2018/templates/page.rs.html
index d8298ea..38233ff 100644
--- a/examples/ed2018/templates/page.rs.html
+++ b/examples/ed2018/templates/page.rs.html
@@ -4,7 +4,7 @@
Example with stylesheet
-
+
Hello world!
diff --git a/examples/gotham/Cargo.toml b/examples/gotham/Cargo.toml
index 935f537..2d013b5 100644
--- a/examples/gotham/Cargo.toml
+++ b/examples/gotham/Cargo.toml
@@ -6,7 +6,7 @@ authors = ["Rasmus Kaj "]
build = "src/build.rs"
[build-dependencies]
-ructe = { path = "../..", features = ["sass", "mime03"] }
+ructe = { path = "../..", features = ["sass", "sri", "mime03"] }
[dependencies]
gotham = "0.4.0"
diff --git a/examples/gotham/templates/page.rs.html b/examples/gotham/templates/page.rs.html
index 5bb78e8..87a071c 100644
--- a/examples/gotham/templates/page.rs.html
+++ b/examples/gotham/templates/page.rs.html
@@ -8,7 +8,7 @@
Example
-
+
diff --git a/examples/iron/Cargo.toml b/examples/iron/Cargo.toml
index 0781210..2e42834 100644
--- a/examples/iron/Cargo.toml
+++ b/examples/iron/Cargo.toml
@@ -6,7 +6,7 @@ authors = ["Rasmus Kaj "]
build = "src/build.rs"
[build-dependencies]
-ructe = { path = "../..", features = ["sass", "mime02"] }
+ructe = { path = "../..", features = ["sass", "sri", "mime02"] }
[dependencies]
iron = "0.6"
diff --git a/examples/iron/templates/page.rs.html b/examples/iron/templates/page.rs.html
index 0f548ab..d1a7042 100644
--- a/examples/iron/templates/page.rs.html
+++ b/examples/iron/templates/page.rs.html
@@ -8,7 +8,7 @@
Example
-
+
diff --git a/examples/nickel/Cargo.toml b/examples/nickel/Cargo.toml
index 779de5c..eeb13bd 100644
--- a/examples/nickel/Cargo.toml
+++ b/examples/nickel/Cargo.toml
@@ -6,7 +6,7 @@ authors = ["Rasmus Kaj "]
build = "src/build.rs"
[build-dependencies]
-ructe = { path = "../..", features = ["sass", "mime02"] }
+ructe = { path = "../..", features = ["sass", "sri", "mime02"] }
[dependencies]
nickel = "0.11.0"
diff --git a/examples/nickel/templates/page.rs.html b/examples/nickel/templates/page.rs.html
index a8130b2..27f1fbc 100644
--- a/examples/nickel/templates/page.rs.html
+++ b/examples/nickel/templates/page.rs.html
@@ -8,7 +8,7 @@
Example
-
+
diff --git a/examples/static-sass/Cargo.toml b/examples/static-sass/Cargo.toml
index 80dfce7..a4186e5 100644
--- a/examples/static-sass/Cargo.toml
+++ b/examples/static-sass/Cargo.toml
@@ -6,4 +6,4 @@ authors = ["Rasmus Kaj "]
build = "src/build.rs"
[build-dependencies]
-ructe = { path = "../..", features = ["sass"] }
+ructe = { path = "../..", features = ["sass", "sri"] }
diff --git a/examples/static-sass/templates/page.rs.html b/examples/static-sass/templates/page.rs.html
index d8298ea..38233ff 100644
--- a/examples/static-sass/templates/page.rs.html
+++ b/examples/static-sass/templates/page.rs.html
@@ -4,7 +4,7 @@
Example with stylesheet
-
+
Hello world!
diff --git a/examples/statics/Cargo.toml b/examples/statics/Cargo.toml
index 2f2d3ba..1ff8300 100644
--- a/examples/statics/Cargo.toml
+++ b/examples/statics/Cargo.toml
@@ -6,7 +6,7 @@ authors = ["Rasmus Kaj "]
build = "src/build.rs"
[build-dependencies]
-ructe = { path = "../..", features = ["mime03"] }
+ructe = { path = "../..", features = ["sri", "mime03"] }
[dependencies]
mime = "0.3.0"
diff --git a/examples/statics/templates/page.rs.html b/examples/statics/templates/page.rs.html
index d8298ea..38233ff 100644
--- a/examples/statics/templates/page.rs.html
+++ b/examples/statics/templates/page.rs.html
@@ -4,7 +4,7 @@
Example with stylesheet
-
+
Hello world!
diff --git a/examples/warp02/Cargo.toml b/examples/warp02/Cargo.toml
index 8bb401f..0b1cc0f 100644
--- a/examples/warp02/Cargo.toml
+++ b/examples/warp02/Cargo.toml
@@ -7,7 +7,7 @@ edition = "2018"
build = "src/build.rs"
[build-dependencies]
-ructe = { path = "../..", features = ["warp02", "sass"] }
+ructe = { path = "../..", features = ["warp02", "sass", "sri"] }
[dependencies]
warp = "0.2.0"
diff --git a/examples/warp02/templates/error.rs.html b/examples/warp02/templates/error.rs.html
index 8ce6610..0fc5796 100644
--- a/examples/warp02/templates/error.rs.html
+++ b/examples/warp02/templates/error.rs.html
@@ -9,7 +9,7 @@
Error @code.as_u16(): @code.canonical_reason().unwrap_or("error")
-
+
diff --git a/examples/warp02/templates/page.rs.html b/examples/warp02/templates/page.rs.html
index c8ad365..8e61adb 100644
--- a/examples/warp02/templates/page.rs.html
+++ b/examples/warp02/templates/page.rs.html
@@ -8,7 +8,7 @@
Example
-
+
diff --git a/src/lib.rs b/src/lib.rs
index f36ab30..396f324 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -90,6 +90,9 @@
//! version 0.2.x of the [mime] crate.
//! * `warp02` -- Provide an extension to [`Response::Builder`] to
//! simplify template rendering in the [warp] framework, versions 0.2.x.
+//! * `sri` -- Generate [subresource
+//! integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity)
+//! strings using the [ssri] crate
//!
//! [`response::Builder`]: ../http/response/struct.Builder.html
//! [mime]: https://crates.rs/crates/mime
@@ -103,7 +106,7 @@
//! build = "src/build.rs"
//!
//! [build-dependencies]
-//! ructe = { version = "0.6.0", features = ["sass", "mime03"] }
+//! ructe = { version = "0.6.0", features = ["sass", "sri", "mime03"] }
//!
//! [dependencies]
//! mime = "0.3.13"
@@ -118,6 +121,8 @@ extern crate mime;
extern crate nom;
#[cfg(feature = "sass")]
extern crate rsass;
+#[cfg(feature = "sri")]
+extern crate ssri;
pub mod Template_syntax;
mod expression;
diff --git a/src/staticfiles.rs b/src/staticfiles.rs
index 7434693..9a63afa 100644
--- a/src/staticfiles.rs
+++ b/src/staticfiles.rs
@@ -2,6 +2,7 @@ use super::Result;
use base64;
use itertools::Itertools;
use md5;
+use std::borrow::Cow;
use std::collections::BTreeMap;
use std::fmt::{self, Display};
use std::fs::{read_dir, File};
@@ -225,6 +226,11 @@ pub struct StaticFile {
if cfg!(feature = "mime03") {
src.write_all(b" pub mime: &'static Mime,\n")?;
}
+ if cfg!(feature = "sri") {
+ src.write_all(b" pub integrity_sha256: &'static str,\n")?;
+ src.write_all(b" pub integrity_sha384: &'static str,\n")?;
+ src.write_all(b" pub integrity_sha512: &'static str,\n")?;
+ }
src.write_all(
b"}
#[allow(dead_code)]
@@ -332,7 +338,7 @@ impl StaticFile {
&path,
&rust_name,
&url_name,
- &FileContent(&path),
+ FileContent(&path),
ext,
)?;
}
@@ -350,7 +356,7 @@ impl StaticFile {
let path = &self.path_for(path);
let ext = name_and_ext(path).map(|(_, e)| e).unwrap_or("");
println!("cargo:rerun-if-changed={}", path.display());
- self.add_static(path, url_name, url_name, &FileContent(path), ext)?;
+ self.add_static(path, url_name, url_name, FileContent(path), ext)?;
Ok(())
}
@@ -407,7 +413,7 @@ impl StaticFile {
path,
&rust_name,
&url_name,
- &ByteString(data),
+ ByteString(data),
ext,
)?;
}
@@ -474,14 +480,17 @@ impl StaticFile {
self.add_file_data(&src.with_extension("css"), &css)
}
- fn add_static(
+ fn add_static<'a, C>(
&mut self,
path: &Path,
rust_name: &str,
url_name: &str,
- content: &impl Display,
+ content: C,
suffix: &str,
- ) -> io::Result<()> {
+ ) -> io::Result<()>
+ where
+ C: Display + Into>,
+ {
let rust_name = rust_name
.replace("/", "_")
.replace("-", "_")
@@ -494,12 +503,14 @@ impl StaticFile {
\n content: {content},\
\n name: \"{url_name}\",\
\n{mime}\
+ \n{sri}\
}};",
path = path,
rust_name = rust_name,
url_name = url_name,
- content = content,
+ content = format!("{}", content),
mime = mime_arg(suffix),
+ sri = sri_args(&content.into()),
)?;
self.names.insert(rust_name.clone(), url_name.into());
self.names_r.insert(url_name.into(), rust_name);
@@ -560,6 +571,22 @@ impl<'a> Display for FileContent<'a> {
}
}
+impl<'a> From> for Cow<'a, [u8]> {
+ fn from(file_content: FileContent<'a>) -> Self {
+ let mut bytes = vec![];
+ let mut file = File::open(file_content.0).expect(&format!(
+ "path does not exist: {}",
+ file_content.0.display()
+ ));
+ file.read_to_end(&mut bytes).expect(&format!(
+ "could not read file: {}",
+ file_content.0.display()
+ ));
+
+ Cow::Owned(bytes)
+ }
+}
+
struct ByteString<'a>(&'a [u8]);
impl<'a> Display for ByteString<'a> {
@@ -581,6 +608,12 @@ impl<'a> Display for ByteString<'a> {
}
}
+impl<'a> From> for Cow<'a, [u8]> {
+ fn from(bytes: ByteString<'a>) -> Self {
+ Cow::Borrowed(bytes.0)
+ }
+}
+
fn name_and_ext(path: &Path) -> Option<(&str, &str)> {
if let (Some(name), Some(ext)) = (path.file_name(), path.extension()) {
if let (Some(name), Some(ext)) = (name.to_str(), ext.to_str()) {
@@ -594,6 +627,41 @@ fn name_and_ext(path: &Path) -> Option<(&str, &str)> {
fn checksum_slug(data: &[u8]) -> String {
base64::encode_config(&md5::compute(data)[..6], base64::URL_SAFE)
}
+
+#[cfg(not(feature = "sri"))]
+fn sri_args(_: &[u8]) -> String {
+ "".to_string()
+}
+
+#[cfg(feature = "sri")]
+fn sri_args(content: &[u8]) -> String {
+ use ssri::{Algorithm, IntegrityOpts};
+
+ let sha256 = IntegrityOpts::new()
+ .algorithm(Algorithm::Sha256)
+ .chain(content)
+ .result()
+ .to_string();
+
+ let sha384 = IntegrityOpts::new()
+ .algorithm(Algorithm::Sha384)
+ .chain(content)
+ .result()
+ .to_string();
+ let sha512 = IntegrityOpts::new()
+ .algorithm(Algorithm::Sha512)
+ .chain(content)
+ .result()
+ .to_string();
+
+ format!(
+ "integrity_sha256: {sha256:?},\nintegrity_sha384: {sha384:?},\nintegrity_sha512: {sha512:?}",
+ sha256 = sha256,
+ sha384 = sha384,
+ sha512 = sha512,
+ )
+}
+
#[cfg(not(feature = "mime02"))]
#[cfg(not(feature = "mime03"))]
fn mime_arg(_: &str) -> String {